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


131

有没有什么办法map/ reduce/ filter的/ etc Set中的JavaScript或者我会写我自己?

这是一些明智的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;
}

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


映射减少a Set的问题在于Set不是Functor。
Bartek Banachewicz

@BartekBanachewicz是的,这是个问题...对吗?
谢谢您

2
好吧,考虑一下var s = new Set([1,2,3,4]); s.map((a) => 42);。它改变了元素的数量,map通常不应该这样做。更糟糕的是,如果只比较保留的对象的一部分,因为从技术上讲,这并不确定要获得哪个对象。
Bartek Banachewicz

我曾经考虑过,但是我不确定(个人)会认为那是无效的。好的,至少forEach在这种情况下存在,但是为什么不reduce呢?
谢谢您

Answers:


105

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

然后,您可以使用所有数组功能。

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

1
因为该功能不适用于Set!这是本主题中尚不存在的完整,指导和理解的解决方法。在Set实现这些功能之前,“花费更长的时间”这一事实是为解决此问题付出了可悲的代价!
ZephDavies

1
这是什么和Array.from之间的差异
皮特

9
至少对我来说,这和TypeScript 的区别Array.fromArray.from。使用[...mySet]会产生错误:TS2461: Type 'Set<number>' is not an array type.
Mikal Madsen

1
有关spread与Array.from()的信息,请参见stackoverflow.com/a/40549565/5516454 基本上,两者均可在此处使用。Array.from()可以另外执行不实现该@@iterator方法的类似数组的对象。
ZephDavies

仍然不适用于打字稿。我得到了ERROR TypeError: this.sausages.slice is not a function
Simon_Weaver

22

综上所述,从意见的讨论:虽然没有技术上的原因对于设置为具备reduce,这不是目前提供,我们只能希望它在ES7变化。

至于map,单独调用它可能会违反Set约束,因此它在此处的存在可能值得商bat。

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

如果您可以违反该规定,因为例如您将要折叠,则可以map在将每个零件传递给之前将其应用到每个元素上reduce,从而接受中间集合(此时不是Set)要减少可能有重复的元素。这本质上等效于转换为Array进行处理。


1
除了(使用上面的代码)s.map(a => 42)会导致Set { 42 }映射结果的长度不同,而不会出现“重复的”元素外,这通常很好。也许更新措辞,我会接受这个答案。
谢谢你

@naomik哦,亲爱的,我刚写完我的第一杯咖啡。从第二个角度看,传递给reduce的中间集合可能会包含立即元素,如果您接受它不是集合的话-那是我的意思。
Bartek Banachewicz

哦,我明白了-映射必须映射到相同的类型,因此目标集中可能发生冲突。当我发现这个问题时,我想将map映射到一组数组中。(就像您设置了set.toArray()。map()`一样)
Simon_Weaver

2
在Scala和Haskell中,集合支持地图操作-它可以减少集合中的元素数量。
Velizar Hristov

8

缺乏的原因map/ reduce/ filterMap/ 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]));

一种替代是,以指定映射/降低/过滤器可迭代/迭代器协议的一部分,因为entries/ values/ keys回报Iterator秒。尽管不是每个可迭代对象也都是“可映射的”,但可以想象。另一个选择是为此指定一个单独的“收集协议”。

但是,我不知道ES上有关此主题的当前讨论。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.