使用Reduce代替链式调用Filter和Map

5
我有一个函数,它接受一个狗对象数组,并返回所有主人的名字数组。
    dogs = [
      {name: 'Archie', breed: 'Lurcher', owner: 'Jack'},
      {name: 'Charlie', breed: 'Pug', owner: 'John'},
      {name: 'Buddy', breed: 'Pug', owner: 'Mike'}
    ]

我通过链式使用filter和map方法得到了结果,但这意味着我要对相同的列表进行两次循环遍历。我知道可以使用Reduce方法来完成这个任务,但不确定该如何做。在使用Reduce时大多数例子似乎都是针对数字,这让我在处理自己的问题时有些困惑。

如果有任何帮助,将不胜感激。谢谢。

function getOwners(dogs) {

   return dogs.filter(dog => dog.breed === 'Pug').map(dog => dog.owner);
}

  returns ['John', 'Mike']

2个回答

12
您需要查找的功能是:

您要查找的函数为:

dogs.reduce((total, current) => current.breed === "Pug" ? [...total, current.owner] : total, []);

我们检查每只狗的品种是否为“Pug” (current.breed === "Pug"),如果是,则将该狗的主人添加到total数组中;否则,我们保持total数组不变。

3
使用 concat 而不是展开操作符可以提高效率:total.concat(current.owner) : total - Jack Bashford
1
我该如何在TypeScript中实现这个?当我尝试将acc变成数字数组,例如reduce((total: number[], line: string, i: number)时,我会得到错误信息:Argument of type '(total: number[], line: string, i: number) => number[]' is not assignable to parameter of type '(previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string'. 我可以得到正确的输出结果,但是这个错误会停止执行。 - Mote Zart

0
你传递了一个初始数组,在规约时仅在值未被过滤掉的情况下推送映射值。
const filteredMap = (filter, mapping) => {
  return arr => Array.prototype.reduce.call(arr, (accumulator, value) => {
    if (filter(value)) accumulator.push(mapping(value));
    return accumulator;
  }, []);
};

const getOwners = filteredMap(dog => dog.breed == 'Pug', dog => dog.owner);

getOwners([
  {name: 'Archie', breed: 'Lurcher', owner: 'Jack'},
  {name: 'Charlie', breed: 'Pug', owner: 'John'},
  {name: 'Buddy', breed: 'Pug', owner: 'Mike'}
]);  // => [ 'John', 'Mike' ]

不过以循环的形式写出来可能更清晰:

const filteredMap = (filter, mapping) => {
  return arr => {
    const res = [];
    for (const value of arr) {
        if (filter(value)) res.push(mapping(value));
    }
    return res;
  };
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接