如何在 JavaScript 中映射/减少/过滤集合?

问题描述 投票:0回答:6

有没有办法在 JavaScript 中实现

map
/
reduce
/
filter
/等
Set
或者我必须自己编写?

这里有一些明智的

Set.prototype
扩展

Set.prototype.map = function map(f) {
  var newSet = new Set();
  for (var v of this.values()) newSet.add(f(v));
  return newSet;
};

Set.prototype.reduce = function(f,initial) {
  var result = initial;
  for (var v of this) result = f(result, v);
  return result;
};

Set.prototype.filter = function filter(f) {
  var newSet = new Set();
  for (var v of this) if(f(v)) newSet.add(v);
  return newSet;
};

Set.prototype.every = function every(f) {
  for (var v of this) if (!f(v)) return false;
  return true;
};

Set.prototype.some = function some(f) {
  for (var v of this) if (f(v)) return true;
  return false;
};

我们来一组吧

let s = new Set([1,2,3,4]);

还有一些愚蠢的小功能

const times10 = x => x * 10;
const add = (x,y) => x + y;
const even = x => x % 2 === 0;

看看它们是如何工作的

s.map(times10);    //=> Set {10,20,30,40}
s.reduce(add, 0);  //=> 10
s.filter(even);    //=> Set {2,4}
s.every(even);     //=> false
s.some(even);      //=> true

这不是很好吗?是的,我也这么认为。将其与丑陋的迭代器用法进行比较

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(v));
}

还有

// barf
let sum = 0;
for (let v in s) {
  sum = sum + v;
}

有没有更好的方法在 JavaScript 中使用

map
来完成
reduce
Set

javascript set ecmascript-6 reduce
6个回答
256
投票

一种简便的方法是通过 ES6 扩展运算符将其转换为数组。

然后所有数组功能都可以使用了。

const mySet = new Set([1,2,3,4]);
[...mySet].reduce(...);

29
投票

总结一下评论中的讨论:虽然没有技术原因设置为not

reduce
,但目前还没有提供,我们只能希望它在ES7中有所改变。

至于

map
,单独调用它可能会违反
Set
约束,因此它在这里的存在可能是有争议的。

考虑使用函数进行映射

(a) => 42
- 它将把集合的大小更改为 1,这可能也可能不是是你想要的。

如果您同意违反该规定,因为例如无论如何你都要折叠,你可以在将每个元素传递给

map
之前将
reduce
部分应用于每个元素,从而接受中间集合(此时不是 Set)被减少可能有重复的元素。这本质上就相当于转成Array来做处理。


18
投票

map
/
reduce
集合上缺少
filter
/
Map
/
Set
的原因似乎主要是概念问题。 Javascript 中的每个集合类型实际上是否应该指定自己的迭代方法只是为了允许这样做

const mySet = new Set([1,2,3]);
const myMap = new Map([[1,1],[2,2],[3,3]]);

mySet.map(x => x + 1);
myMap.map(([k, x]) => [k, x + 1]);

而不是

new Set(Array.from(mySet.values(), x => x + 1));
new Map(Array.from(myMap.entries(), ([k, x]) => [k, x + 1]));

另一种方法是将 map/reduce/filter 指定为可迭代/迭代器协议的一部分,因为

entries
/
values
/
keys
返回
Iterator
s。但可以想象的是,并非每个可迭代对象都是“可映射的”。另一种选择是为此目的指定一个单独的“收集协议”。

但是,我不知道 ES 目前关于这个话题的讨论。


0
投票

认为这在速度方面值得一提,如果您正在选择 Set 方法

forEach()
(为每个元素调用回调)或
values()
(返回包含 Set 中所有值的迭代器)。

举个例子,让我们从集合中筛选出偶数:

const generateSet = (n, m) => {
  // Generate list of length n with random numbers between 0 and m
  let arr = Array.from({ length: n }, () =>
    Math.floor(Math.random() * m)
  );
  // Convert to Set
  var set = new Set(arr);
  return set;
};

我们的两个过滤功能:

const filterValues = (set) => {
  // Using Iterator
  const it = set.values();
  let result = it.next();
  while (!result.done) {
    if (result.value % 2 === 0) {
      set.delete(result.value);
    }
    result = it.next();
  }
};

const filterForEach = (set) => {
  // invokes a callback
  set.forEach((item) => {
    if (item % 2 === 0) {
      set.delete(item);
    }
  });
};

对于计时,我们使用对随机数组进行计时,包括范围 [0, 10,000,000] 中包含 5,000,000 个项目的数字:

let setValues = generateSet(5000000, 10000000);
console.time("Filter with values()");
filterValues(setValues);
console.timeEnd("Filter with values()");

let setForEach = generateSet(5000000, 10000000);
console.time("Filter with forEach()");
filterForEach(setForEach);
console.timeEnd("Filter with forEach()");

结果是:

Filter with values(): 399.456ms
Filter with forEach(): 374.698ms

或者你可以坚持使用数组方法:

const arrMethod = (set) => {
  // Using Array method
  const filter = [...set].filter((item) => item % 2 === 0);
  return filter;
};

let setArray = generateSet(5000000, 10000000);
console.time("Filter with array");
filterForEach(setArray);
console.timeEnd("Filter with array");

似乎始终更快...

Filter with values(): 356.486ms
Filter with forEach(): 386.825ms
Filter with array: 342.358ms

0
投票

从 ECMAScript 2025 开始,类似的方法现在作为 迭代器辅助方法 存在。这意味着您可以在函数表达式中使用它们,同时仍然避免创建中间数组。

new Set(s.values().map(times10));  //=> Set {10,20,30,40}
s.values().reduce(add, 0);         //=> 10
new Set(s.values().filter(even));  //=> Set {2,4}
s.values().every(even);            //=> false
s.values().some(even);             //=> true

-2
投票
const set = new Set([1,2,3,4,5]);

function filterSet(index) {
    set.delete([...set][index]);
}

filterSet(3); // Set { 1, 2, 3, 5, [size]: 4 }

认为这是“过滤”集合的一个相当不错的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.