在Javascript中同时映射和过滤数组

Dan*_*ori 111 javascript arrays

我有一个对象数组,我想迭代生成一个新的过滤数组.但是,我还需要根据参数从新数组中过滤掉一些对象.我正在尝试这个:

function renderOptions(options) {
    return options.map(function (option) {
        if (!option.assigned) {
            return (someNewObject);
        }
    });   
}
Run Code Online (Sandbox Code Playgroud)

这是一个好方法吗?有更好的方法吗?我愿意使用任何库,如lodash.

the*_*eye 158

你应该用Array.reduce它.

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = options.reduce(function(filtered, option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     filtered.push(someNewValue);
  }
  return filtered;
}, []);

document.getElementById('output').innerHTML = JSON.stringify(reduced);
Run Code Online (Sandbox Code Playgroud)
<h1>Only assigned options</h1>
<pre id="output"> </pre>
Run Code Online (Sandbox Code Playgroud)

更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

  • 对我来说,第一个参数 `filtered` 是一个对象。所以`filtered.push` 对我来说是未定义的。 (4认同)
  • 我也遇到了`filtered`作为对象的问题.这是因为我没有在reduce函数之后传入"初始值" - 空数组(`[]`).例如,不正确的`var reduced = options.reduce(function(filtered,option){...});`correct`var reduced = options.reduce(function(filtered,option){...},[]);` (3认同)

Zuk*_*ker 40

使用减少,卢克!

function renderOptions(options) {
    return options.reduce(function (res, option) {
        if (!option.assigned) {
            res.push(someNewObject);
        }
        return res;
    }, []);   
}
Run Code Online (Sandbox Code Playgroud)


Nat*_*iaz 14

使用ES6,您可以做到非常简短:

options.filter(opt => !opt.assigned).map(opt => someNewObject)

  • 这将执行两个循环,具体取决于哪些过滤器返回仍然可以占用内存. (19认同)
  • 这种方式很好,但这个问题是**同时映射和过滤数组**最好的方法是`options.reduce(res,options....`回答@Zuker (5认同)
  • @hogan,但这是原始查询的正确答案(可能不是最佳解决方案) (2认同)
  • 这并不完全正确,因为在这里您丢失了值最初定位的索引,并且该信息在运行映射 fn 时非常有用。 (2认同)

mat*_*sve 12

我会发表评论,但我没有所需的声誉。对马克西姆·库兹明 (Maxim Kuzmin) 的其他非常好的答案进行了小幅改进,使其更有效率:

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, { name, assigned }) => assigned ? result.push(name) && result : result, []);

console.log(filtered);
Run Code Online (Sandbox Code Playgroud)

解释

我们不是在每次迭代中一遍又一遍地传播整个结果,而是只附加到数组中,并且仅在实际有值要插入时才附加。


Tre*_*xon 11

Array.prototype.flatMap是另一个选项。

options.flatMap(o => o.assigned ? [o.name] : []);
Run Code Online (Sandbox Code Playgroud)

在上面链接的MDN页面上:

flatMap可以用作在地图中添加和删除项目(修改项目数)的方法。换句话说,它允许您将许多项目映射到许多项目(通过分别处理每个输入项目),而不是始终一对一。从这个意义上讲,它的作用类似于过滤器。只需返回一个1元素数组以保留该项目,返回一个多元素数组以添加项目,或返回0元素数组以删除该项目。

  • 也许是因为我有更多的面向对象经验,但这个答案对我来说似乎比“reduce”更直观。 (4认同)
  • 与使用 2 个循环(通过分别调用 array.filter 和 array.map)相比,性能如何? (3认同)
  • 很好的解决方案!但是,读者切记,`flatMap`是最近才引入的,它的浏览器支持是[受限的](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap# Browser_compatibility)。您可能要使用官方的polyfill / shims:[flatMap](https://github.com/es-shims/Array.prototype.flatMap)和[flat](https://github.com/es-shims/ Array.prototype.flat)。 (2认同)

Max*_*min 8

reduceES6花式扩展语法的一行就在这里!

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

const filtered = options
  .reduce((result, {name, assigned}) => [...result, ...assigned ? [name] : []], []);

console.log(filtered);
Run Code Online (Sandbox Code Playgroud)

  • 好一个。我花了一秒钟才意识到“...分配”发生了什么?[名称] : []` - 它可能更易读为 `...(已分配 ? [名称] : [])` (3认同)
  • 真的很好@Maxim!我赞成这个!但是...在添加的每个元素上,它必须将所有元素分布在 `result` 中...就像 `filter(assigned).map(name)` 解决方案 (2认同)

Ste*_*uan 6

我通过以下几点优化了答案:

  1. 重写if (cond) { stmt; }cond && stmt;
  2. 使用 ES6箭头函数

我将提出两种解决方案,一种使用forEach,另一种使用reduce

解决方案 1:使用 forEach

该解决方案的工作原理是使用forEach迭代每个元素。然后,在循环体中forEach,我们使用条件作为过滤器,它决定我们是否要向结果数组附加某些内容。

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
const reduced = [ ];
options.forEach(o => {
  o.assigned && reduced.push( { name: o.name, newProperty: 'Foo' } );
} );
console.log(reduced);
Run Code Online (Sandbox Code Playgroud)

解决方案2:使用reduce

该解决方案使用Array.prototype.reduce而不是forEach迭代数组。它认识到内置了初始化程序和循环机制的事实reduce。除此之外,此解决方案与解决forEach方案或多或少相同,因此,差异归结为修饰语法糖。

const options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];
const reduced = options.reduce((a, o) => {
  o.assigned && a.push( { name: o.name, newProperty: 'Foo' } );
  return a;
}, [ ] );
console.log(reduced);
Run Code Online (Sandbox Code Playgroud)

我让您决定采用哪种解决方案。


Amm*_*CSE 5

使用Array.prototy.filter本身

function renderOptions(options) {
    return options.filter(function(option){
        return !option.assigned;
    }).map(function (option) {
        return (someNewObject);
    });   
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您想要的值是在执行过滤器的过程中计算出来的,则此方法效果不佳;您必须复制该部分代码。 (2认同)

Dan*_*iel 5

在某些时候,是不是更容易(或同样容易)使用 forEach

var options = [
  { name: 'One', assigned: true }, 
  { name: 'Two', assigned: false }, 
  { name: 'Three', assigned: true }, 
];

var reduced = []
options.forEach(function(option) {
  if (option.assigned) {
     var someNewValue = { name: option.name, newProperty: 'Foo' }
     reduced.push(someNewValue);
  }
});

document.getElementById('output').innerHTML = JSON.stringify(reduced);
Run Code Online (Sandbox Code Playgroud)
<h1>Only assigned options</h1>
<pre id="output"> </pre>
Run Code Online (Sandbox Code Playgroud)

但是,如果有一个malter()orfap()函数结合了mapandfilter函数,那就太好了。它会像过滤器一样工作,除了返回 true 或 false 之外,它会返回任何对象或 null/undefined。

  • 你可能想检查一下你的表情包... ;-) (3认同)
  • 但是这样你就无法炫耀你的 1 行 133t JS,除了你之外没有人理解。 (3认同)
  • 这些名字是黄金,您应该向 TC39 提交提案 (2认同)