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

如何在JavaScript中映射/减少/过滤集合?,javascript,set,ecmascript-6,reduce,Javascript,Set,Ecmascript 6,Reduce,有没有办法用JavaScript来映射/减少/过滤器/etc集合,或者我必须自己编写 下面是一些合理的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) { va

有没有办法用JavaScript来映射/
减少
/
过滤器
/etc
集合
,或者我必须自己编写

下面是一些合理的
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
那不是很好吗?是的,我也这么认为。将其与丑陋的迭代器用法进行比较


使用JavaScript中的
Set
来完成
map
reduce
有没有更好的方法?

从评论中总结一下讨论:虽然Set没有
reduce
的技术原因,但目前还没有提供,我们只能希望它在ES7中有所改变

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

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


如果您同意违反该规则,例如,您无论如何都要折叠,那么您可以在每个元素上应用
映射
部分,然后将它们传递给
reduce
,从而接受将要缩减的中间集合(此时不是集合)可能有重复的元素。这本质上相当于转换为数组进行处理。

映射
/
集合上缺少
映射
/
减少
/
过滤器
的原因似乎主要是概念上的问题。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作为iterable/iterator协议的一部分,因为
条目
/
/
返回
迭代器
s。可以想象的是,并非每个iterable都是“可映射的”。另一种选择是为此目的指定一个单独的“收集协议”


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

一种简单的方法是通过ES6 spread操作符将其转换为数组

然后所有的数组函数都可以使用

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

MAP减少A<代码> SET <代码>的问题是,集合不是函子。好,考虑<代码> var var =新集合([1,2,3,4]);s、 map((a)=>42)。它改变了元素的数量,而
map
通常不应该这样做。更糟糕的是,如果你只是比较部分被保存的物体,因为从技术上说,你不知道你将得到哪一个。我已经考虑过了,但是我不确定我(个人)会认为那是无效的。好吧,那么至少该场景中存在
forEach
,但为什么没有
reduce
呢?我认为这是一个疏忽。折叠(减少)一个集合是非常好的。一些相关阅读:这基本上是好的,除了(使用上面的代码),
s.map(a=>42)
将导致
Set{42}
因此映射结果将是不同的长度,但不会有“重复”元素。也许更新一下措辞,我会接受这个答案。@naomik Oh derp写这封信时,我刚喝完第一杯咖啡。在第二种情况下,如果您接受传递给reduce的中间集合不是一个集合,那么它可能具有立即元素——这就是我的意思。哦,我明白了——map必须映射到同一类型,因此目标集合中可能会发生冲突。当我发现这个问题时,我想map应该从一个集合映射到一个数组。(就像您设置了.toArray().map()一样)`在Scala和Haskell中,集合支持映射操作-它可以减少集合中的元素数量。因为函数不适用于集合!这是一个完整的、有指导性的、可理解的解决方案,但在本主题中尚未出现。在集合实现这些功能之前,为解决方案付出“更长”的时间是一个令人悲哀的代价!W这就是它和数组之间的区别。至少对我来说,这和
数组之间的区别。from
数组。from
与TypeScript一起工作。使用
[…mySet]
会产生错误:
TS2461:Type'Set'不是数组类型。
用于spread vs Array.from(),请参阅,基本上,这两个都可以在这里使用。Array.from()还可以执行类似数组的对象,这些对象不实现
@@iterator
方法。我仍然无法使用typescript。我得到
错误TypeError:this.sausages.slice不是函数
new Set(Array.from(mySet.values(), x => x + 1));
new Map(Array.from(myMap.entries(), ([k, x]) => [k, x + 1]));
const mySet = new Set([1,2,3,4]);
[...mySet].reduce()