如何解决基于来自另一个数组非静态的键的对象子集数组,然后过滤
let data =
[
{
"region": 28,"nuts": 33,"score" : 26
},{
"region": 18,"nuts": 22,"score" : 21
}
]
另一个数组包含要过滤上述对象并对其进行子集化的键:
// this changes everytime
// it may include ALL the keys or only ONE key of above object
let keysArray = ["region","score"]
(OR )
let keysArray = ["region","nuts"]
现在,如果 data
需要过滤 region
和 score
大于 25,那么它应该返回以下内容:
// when => keysArray = ["region","score"]
// region && score >= 25
// expected output
[
{
"region": 28,"score" : 26
}
]
// when => keysArray = ["region","nuts"]
// region && nuts >= 25
// expected output
[
{
"region": 28,"nuts" : 33
}
]
怎样才能做到这一点? 提前致谢。
PS:这不能回答问题- filtering an array of objects based on another array in javascript
解决方法
我创建了一个工作片段。
let data = [{
region: 28,nuts: 33,score: 26
},{
region: 18,nuts: 22,score: 21
}
];
// this changes everytime
// it may include ALL the keys or only ONE key of above object
let keysArray = ["region","score"];
// Write Javascript code!
const appDiv = document.getElementById("app");
appDiv.innerHTML = `<h1>JS Starter</h1>`;
const resultDiv = document.getElementById("result");
filterData(keysArray,25).forEach(item => {
resultDiv.innerHTML += `<li>region: ${item.region},score: ${
item.score
}</li>`;
});
function filterData(keys,val) {
let result = [];
data.forEach(dataItem => {
let isEligible = false;
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
isEligible = dataItem[key] >= val;
if (!isEligible) break;
}
if (isEligible) result.push(dataItem);
});
return result;
}
<div id="app"></div>
<ul id="result"></ul>
stackblitz 中的工作演示: https://stackblitz.com/edit/js-swutjy?embed=1&file=index.js
,let data =
[
{
"region": 28,"nuts": 33,"score" : 26
},{
"region": 18,"nuts": 22,"score" : 21
}
];
let keysArray = ["region","nuts"];
const hasValAboveThreshold = (item,threshold) => {
return keysArray.every((key) => item[key] > threshold);
}
const getItem = (item) => {
return keysArray.reduce((acc,key) => {
acc[key] = item[key];
return acc;
},{});
}
const output = data.reduce((acc,val) => {
if (hasValAboveThreshold(val,25)) {
acc.push(getItem(val));
}
return acc;
},[]);
console.log(output);
使用 Underscore(或 Lodash),您可以编写更短的代码。
// The function that will do the trick.
function subsetFilter(data,keys,predicate) {
return _.chain(data)
.map(_.partial(_.pick,_,keys))
.filter(_.partial(_.every,predicate))
.value();
}
// Usage.
subsetFilter(data,['region','nuts'],x => x >= 25);
让我们把它分成几部分。
function subsetFilter(data,predicate) {
我们创建了一个带有三个参数的函数:要进行子集化和过滤的数据、可能是可变的键数组,以及 predicate
,它可以是任何返回布尔值的函数。例如,我在上面使用了 x => x >= 25
来说明属性应该大于或等于 25。
_.chain(data)
我们将数据包装在一个特殊的包装器对象中,这将允许我们链接多个 Underscore 函数,每个函数处理链中前一个函数的结果 (doc)。
_.pick
这是一个带有两个参数的函数:一个对象和一个键数组。它返回对象的副本,其中仅包含名称列在键数组 (doc) 中的属性。例如:
_.pick({a: 1,b: 2},['a','c']) // returns {a: 1}
接下来,
_.partial(_.pick,keys)
这会将 _.pick
转换为一个新函数,其中第二个参数已经预先填充了 keys
。 _
表示 _.pick
的这个修改版本仍在寻找它的第一个参数 (doc)。等价于如下函数:
function(obj) {
return _.pick(obj,keys);
}
完成这一行,
.map(_.partial(_.pick,keys))
我们在链接的 data
包装器上调用 _.map
,生成一个新数组,其中每个元素都通过我们使用 _.partial
和 _.pick
创建的函数进行了转换。在我们的示例中,链包装器此时包含以下数据:
[
{
"region": 28,"nuts": 33
},"nuts": 22
}
]
(所以基本上仍然是相同的数据,除了 score
属性不再存在,因为我们没有 _.pick
它们。)
_.partial(_.every,predicate)
同样,我们使用 _.partial
创建一个函数的修改版本,其中第二个参数已经被预填充。这一次,基函数是 _.every
,它接受一个数组或对象和一个谓词。如果谓词为数组的每个元素或对象的每个属性返回 true
,则返回 true
,否则返回 false
。预填充的第二个参数是 predicate
函数的 subsetFilter
参数。基本上我们在这里说明以下内容:给这个函数一个对象,它会告诉你它的所有属性是否都满足 predicate
。
.filter(_.partial(_.every,predicate))
我们使用我们刚刚使用 _.partial
和 _.every
创建的函数_.filter
我们链中的中间结果。我们只保留数组中每个属性满足 predicate
的对象。此时,我们的链包装器包含以下值:
[
{
"region": 28,"nuts": 33
}
]
这是你想要的结果,但它仍然被包装。因此,作为最后一步,我们移除包装器 (doc):
.value();
这是我们表达式的结尾,也是我们从函数返回的结果。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。