【问题标题】:Array.filter with more than one conditionalArray.filter 有多个条件
【发布时间】:2016-07-25 03:29:01
【问题描述】:

我有一个对象数组,我想根据条件对其进行过滤和分组。困境来了,因为我有多个条件,并且希望将数组分成几个数组。第一个数组匹配第一个条件,第二个数组匹配第二个条件,...,最后一个数组包含所有不匹配任何条件的对象。

想到这个问题的第一个解决方案是几个 .filter 函数的形式......

var array = [{
  name: 'X',
  age: 18
}, {
  name: 'Y',
  age: 18
}, {
  name: 'Z',
  age: 20
}, {
  name: 'M',
  age: 20
}, {
  name: 'W',
  age: 5
}, {
  name: 'W',
  age: 10
}];
//objects with age 18
var matchedConditional1 = array.filter(function(x){
  return x.age === 18;
});
//objects with age 20
var matchedConditional2 = array.filter(function(x){
  return x.age === 20;
});
//objects with neither age 18 or 20
var matchedNoConditional = array.filter(function(x){
  return (x.age !== 18 && x.age !== 20);
});

但这似乎是多余的,根本无法重用。

于是我修改了Brendan's答案上的函数,得到了这个。

Array.prototype.group = function(f) {
  var matchedFirst = [],
      matchedSecond = [],
      unmatched = [],
      i = 0,
      l = this.length;
  for (; i < l; i++) {
    if (f.call(this, this[i], i)[0]) {
      matchedFirst.push(this[i]);
    } else if (f.call(this, this[i], i)[1]) {
      matchedSecond.push(this[i]);
    } else {
      unmatched.push(this[i]);
    }
  }
  return [matchedFirst, matchedSecond, unmatched];
};
var filteredArray = array.group(function(x){
  return [x.age === 18, x.age === 20];
});

此方法返回一个包含 3 个数组的数组。第一个包含与第一个条件匹配的所有对象,第二个包含与第二个条件匹配的对象,最后一个包含与两者都不匹配的对象。

这种方法的问题在于它仅限于两个条件,因此只有三组对象。这种方法实际上适用于我的特定情况,因为我只有两个条件,但在需要两个以上的情况下是不可重用的。

我希望能够提供尽可能多的条件,并接收该数量的数组以及一个包含不属于任何组的对象的额外数组。

附言。输入和输出不需要是数组,但我认为这更有意义。该方法不必在 .filter 之后建模,它很可能是 .map 函数甚至是 .reduce。任何建议表示赞赏。

编辑:正如@slebetman 所建议的,如果答案允许代码可组合性,那就太好了。

【问题讨论】:

  • 在目标函数中使用 switch case 尝试 array.map。在每种情况下,只需推送到相应的数组?
  • 这种情况最好使用array.forEach,因为它不是可以用链组合的操作
  • 您只想修改代码还是愿意使用任何外部库?
  • 我愿意接受任何允许我传递多个条件并根据条件对数组进行分组的解决方案。一个库就可以了,但 vanilla JS 可以提供更好的可重用性。
  • 呃,只是创建一个matched数组而不是使用索引变量?

标签: javascript arrays loops


【解决方案1】:

我们将使用findIndex 查找匹配条件的索引,并将该元素放入输出的相应数组元素中:

function makeGrouper(...conditions) {

  return function(array) {
    // Make an array of empty arrays for each condition, plus one.
    var results = conditions.map(_ => []).concat([]);

    array.forEach(elt => {
      var condition = conditions.findIndex(condition => condition(elt));
      if (condition === -1) condition = conditions.length;
      results[condition].push(elt);
    });

    return results;
  };

}

或者,如果你是reduce的粉丝:

function makeGrouper(...conditions) {

  return function(array) {
    return array.reduce((results, elt) => {
      var condition = conditions.findIndex(condition => condition(elt));
      if (condition === -1) condition = conditions.length;
      results[condition].push(elt);
      return results;
    }, conditions.map(_ => []).concat([])));  
  };

}

用法:

const grouper = makeGrouper(
  elt => elt.age === 18,
  elt => elt.age === 20
);

console.log(grouper(data));

此解决方案涉及定义一个函数,您向其提供各种过滤器,该过滤器返回一个函数,然后您可以使用该函数进行实际分组。

【讨论】:

    【解决方案2】:

    试试这样的:

    Array.prototype.groups = function(...conditions) {
      return this.reduce(
        (groups, entry) => {
          let indices = [];
    
          conditions.forEach((cond, i) => {
            if (cond(entry)) indices.push(i);
          });
    
          if (indices.length === 0) groups[groups.length - 1].push(entry);
          else indices.forEach(i => groups[i].push(entry));
    
          return groups
        }, 
        Array.apply(null, { length: conditions.length + 1})
          .map(e => [])
      );
    }
    

    在这个解决方案中,如果一个条目匹配多个条件,它将出现在相应数量的组中。

    使用示例:array.groups(x =&gt; x.name === 'X', x =&gt; x.age === 18);

    最终数组中的最后一个元素 - 不匹配的条目。

    【讨论】:

    • 我建议您对代码进行傻瓜式验证以供将来开发人员参考
    猜你喜欢
    • 2020-08-08
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多