javascript - 正确过滤具有多个条件和数组的对象数组
问题描述
我正在创建一个 Todo 应用程序作为学习 PostgreSQL 和 javascript 函数式编程的练习。这个想法是可以有多个容器,每个容器都有单独的过滤器设置,每个都从 Todos 的主列表中读取,然后每个容器根据它的过滤器设置显示它的结果。这实际上都是在 React-Redux 和 Nodejs 中完成的,但我制作了下面的示例以更好地说明问题。
数据:
// Example todos
let a = {
added: "2021-02-01T05:00:00.000Z",
completed: null,
description: "This is just a test.",
name: "testing",
tags: [],
todo_id: 3,
}
let b = {
added: "2021-02-01T05:00:00.000Z",
completed: null,
description: "This is just a test.",
name: "testing",
tags: ["one", "two"],
todo_id: 4
}
let c = {
added: "2021-02-01T05:00:00.000Z",
completed: null,
description: "This is just a test.",
name: "tesin",
tags: ["one"],
todo_id: 5
}
let d = {
added: "2021-02-01T05:00:00.000Z",
completed: '2021-03-01T05:00:00.000Z',
description: "This is just a test.",
name: "testing",
tags: ["two", "testing"],
todo_id: 6
}
// Example filter_group
// This is just to show what the initial state looks like in my React app
const filter_groups = {
"main": {
byTag: 'two',
byName: '',
byCompleted: true
}
}
// Object.keys(filter_groups).map() passes the filter settings down as props
// Just stuck it in a variable for the example
const filter = filter_groups['main']
// Putting example Todos in array
const todo_list = [a,b,c,d]
当前的解决方案非常简单、丑陋且效率低下。它一次应用每个过滤器:
// ------- CURRENT SOLUTION -------
// if user is trying to filter byCompleted, then filter todo_list
// else do nothing todo_list and pass it to the next filter
// (similar thing applies down the chain)
const byCompleted = filter.byCompleted === true ?
todo_list.filter ( (t) => {
return t.completed !== null
})
:
todo_list
//IF user is trying to filter byTag...
const byTag = filter.byTag !== '' ?
// THEN filter the byCompleted array above
byCompleted.filter( (t) => {
// IF there are tags on the Todo...
let matches = t.tags ?
// THEN filter for matches
t.tags.filter( (tag) => {
return tag === filter.byTag
})
:
// ELSE make this an empty array
[]
// return any Todos with match
return matches.length > 0
})
:
byCompleted
// IF user is trying to filter byName...
const byName = filter.byName !== '' ?
// THEN filter byTag Todos
byTag.filter( (t) => {
return t.name === P.filter.byName
})
:
byTag
console.log(byName);
我真的很想知道是否有办法一次应用整个过滤器。在示例中,您将看到过滤器设置为byTag: 'two', byComplete: true
使控制台仅打印一个结果。我过去尝试使用一个过滤器通过将返回所有带有two
标签的东西。
我尝试查看文档并看到过滤器功能中还有更多内容,但它只是没有点击,而且我找不到任何非常适合我的用例的示例。
编辑:这就是代码沙盒上的全部内容:https ://codesandbox.io/s/recursing-almeida-cdz65?file=/src/index.js
解决方案
您在问题中提到了函数编程。
下面的代码采用 currying 方法来解决您的过滤问题。
一旦建立了各个过滤器,将它们链接在一起就是一件简单的事情。每个过滤器函数将返回一个数组供下一个函数操作。
注意:我不得不推断一些细节。
let a = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "testing", tags: [], todo_id: 3, }
let b = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "testing", tags: ["one", "two"], todo_id: 4 }
let c = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "tesin", tags: ["one"], todo_id: 5 }
let d = { added: "2021-02-01T05:00:00.000Z", completed: '2021-03-01T05:00:00.000Z', description: "This is just a test.", name: "testing", tags: ["two", "testing"], todo_id: 6 }
const filter_groups = { "main": { byTag: 'two', byName: '', byCompleted: true } }
const filter = filter_groups['main']
const todo_list = [a,b,c,d]
let byTag = td => td.tags.includes(filter.byTag);
let byName = td => filter.name ? td.tags.includes(filter.name) : true;
let byCompleted = td => filter.byCompleted ? td.completed !== null : false ;
let result = todo_list.filter(byTag).filter(byName).filter(byCompleted);
console.log(result);
编辑:OP 要求在一个代码块中执行此操作。该解决方案如下:
let a = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "testing", tags: [], todo_id: 3, };
let b = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "testing", tags: ["one", "two"], todo_id: 4 };
let c = { added: "2021-02-01T05:00:00.000Z", completed: null, description: "This is just a test.", name: "tesin", tags: ["one"], todo_id: 5 };
let d = { added: "2021-02-01T05:00:00.000Z", completed: '2021-03-01T05:00:00.000Z', description: "This is just a test.", name: "testing", tags: ["two", "testing"], todo_id: 6 };
const todo_list = [a,b,c,d];
const filter_groups = { "main": { tags: 'two', name: '', completed: true } };
let result2 = todo_list.filter(td=>{
return Object.entries(filter_groups['main'])
.filter(([k,v])=>!!v!=false)
.map(f=>!!td[f[0]])
.every(f=>f==true);
});
console.log(result2);
OP 还在他的原始代码和我的“三步”解决方案中提到了对性能的担忧。
为了让您有信心按照您通常的方式编写代码,我使用 jsben.ch.com 进行了一系列测试。下图显示了 OP 的原始代码始终位于 #2 或 #1。“三步”解决方案分享了这一结果。底线是更多的步骤并不一定等同于较差的性能。如果您可以让站点合作,请多次运行测试以了解性能差异。JSBEN.CH 测试
对这些结果持保留态度- 有利于显示比较,但仅此而已。
推荐阅读
- perl - 获取 LWP::Simple 头部请求失败的原因
- kotlin - 如何在辅助构造函数中初始化变量并在kotlin中调用super?
- python - 具有多种产品的 django paypal 返回贝宝测试商店
- compiler-construction - Lexer 是否应该即时返回令牌列表或令牌
- oracle - ORACLE 数据库的初始化在单元测试中
- python - LSTM 自动编码器噪声重建
- sql - 在日期时间范围之间添加行
- amazon-web-services - AWS Athena WAF 日志
- python - 在 x 时间后中断 while 循环,但在同一级别继续 for 循环
- android - 在 Android 通知中禁用声音(不改变视觉行为)