如何在JavaScript中映射/缩小/过滤Set?

Tha*_*you 100 javascript reduce set ecmascript-6

有没有办法在JavaScript中使用map/ reduce/ filter/ etc a 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;
};
Run Code Online (Sandbox Code Playgroud)

我们来看一点吧

let s = new Set([1,2,3,4]);
Run Code Online (Sandbox Code Playgroud)

还有一些愚蠢的小功能

const times10 = x => x * 10;
const add = (x,y) => x + y;
const even = x => x % 2 === 0;
Run Code Online (Sandbox Code Playgroud)

看看它们是如何工作的

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
Run Code Online (Sandbox Code Playgroud)

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

// puke
let newSet = new Set();
for (let v in s) {
  newSet.add(times10(v));
}
Run Code Online (Sandbox Code Playgroud)

// barf
let sum = 0;
for (let v in s) {
  sum = sum + v;
}
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来完成mapreduce使用SetJavaScript?

Zep*_*ies 77

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

然后,您可以使用所有数组函数.

const mySet = new Set([1,2,3,4]);
[...mySet].reduce()
Run Code Online (Sandbox Code Playgroud)

  • 至少对我来说,这和`Array.from`之间的区别在于`Array.from`适用于TypeScript.使用`[... mySet]`给出错误:`TS2461:Type'Set <number>'不是数组类型. (7认同)
  • 至少在 V8 中,另一个区别是“[...mySet]”将因大型集合的堆栈溢出而失败。`Array.from()` 不使用每个元素的堆栈内存,因此当元素数量可能很大时,风险较小。 (4认同)
  • 这和Array.from有什么区别 (2认同)
  • 对于 spread 与 Array.from(),请参阅 /sf/answers/2838469581/ 基本上,两者都可以在这里使用。Array.from() 还可以执行不实现“@@iterator”方法的类数组对象。 (2认同)

Bar*_*icz 21

综上所述,从意见的讨论:虽然是一套没有技术上的原因reduce,这不是目前提供的,我们只能希望它在ES7变化.

至于map单独调用它可能违反Set约束,因此它的存在可能是有争议的.

考虑使用函数进行映射(a) => 42- 它会将集合的大小更改为1,这可能是也可能不是您想要的.

如果你没有违反,因为你要打算折叠,你可以map在传递它们之前在每个元素上应用该部分reduce,从而接受中间集合(此时不是Set)要减少可能有重复的元素.这基本上等同于转换为Array来进行处理.

  • 哦,我明白了 - 地图必须映射到相同的类型,因此目标集中可能会发生冲突。当我发现这个问题时,我在想映射会映射到集合中的数组。(就像你做了 set.toArray().map()` (2认同)
  • 在Scala和Haskell中,集合支持地图操作-它可以减少集合中的元素数量。 (2认同)

小智 7

缺乏map/ reduce/ filteron Map/ Setcollections的原因似乎主要是概念上的问题.如果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]);
Run Code Online (Sandbox Code Playgroud)

代替

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

另一种方法是指定map/reduce/filter作为iterable/iterator协议的一部分,因为entries/ values/ keysreturn Iterators.可以想象,并非每个可迭代也是"可映射的".另一种选择是为此目的指定一个单独的"收集协议".

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

  • _Javascript 中的每个集合类型实际上是否应该指定自己的迭代方法只是为了允许这样做?_ 是的。所有带有 `new Set` 的 `Array.from` 都是一种解决方法,并且比 `myArray.filter(isBiggerThan6Predicate);` 的可读性要差得多。现在我必须编写自己的 `filterSet` 函数,这样我就可以编写干净的代码:` filterSet(setWithNumbers,greaterThan6Predicate);` (2认同)