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

Subset array of object based on keys from another array(not static) and then filter

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

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 需要针对 25 以上的 regionscore 进行过滤,那么它应该 return 以下内容:

// 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, keys, predicate) {

我们创建一个具有三个参数的函数:要进行子集化和过滤的数据、可能可变的键数组以及 predicate,它可以是 return 的任何函数布尔值。比如我上面用x => x >= 25表示属性应该大于等于25.

_.chain(data)

我们将数据包装在一个特殊的包装器对象中,这将允许我们链接多个 Underscore 函数,每个函数处理链中前一个函数的结果 (doc)。

_.pick

这是一个接受两个参数的函数:一个对象和一个键数组。它 return 是对象的副本,仅包含名称列在键数组 (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
   }, 
  {
    "region": 18,
    "nuts": 22
   }
]

(所以基本上仍然是相同的数据,只是 score 属性不再存在,因为我们没有 _.pick 它们。)

_.partial(_.every, _, predicate)

同样,我们使用 _.partial 创建函数的修改版本,其中第二个参数已经预先填充。这一次,基函数是 _.every,它接受一个数组或对象和一个谓词。它returns true 如果谓词returns true 用于数组的每个元素或对象的每个属性,否则false。预填充的第二个参数是 subsetFilter 函数的 predicate 参数。基本上我们在这里声明以下内容:给这个函数一个对象,它会告诉你它的所有属性是否满足 predicate.

.filter(_.partial(_.every, _, predicate))

我们 _.filter 我们链中的中间结果与我们刚刚使用 _.partial_.every 创建的函数。我们只保留数组中每个 属性 满足 predicate 的对象。此时,我们的链包装器包含以下值:

[
  {
    "region": 28,
    "nuts": 33
   }
]

这是你想要的结果,但它仍然是包裹的。因此,作为最后一步,我们删除包装器 (doc):

.value();

这是我们表达式的结尾,也是我们从函数 return 得到的结果。