基于来自另一个数组非静态的键的对象子集数组,然后过滤

问题描述

我有一个需要根据特定值过滤的对象数组:

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 需要过滤 regionscore 大于 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();

这是我们表达式的结尾,也是我们从函数返回的结果。