将 Javascript 迭代器转换为数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28718641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Transforming a Javascript iterator into an array
提问by Stefano
I'm trying to use the new Mapobject from Javascript EC6, since it's already supported in the latest Firefox and Chrome versions.
我正在尝试使用来自 Javascript EC6的新Map对象,因为它已在最新的 Firefox 和 Chrome 版本中得到支持。
But I'm finding it very limited in "functional" programming, because it lacks classic map, filter etc. methods that would work nicely with a [key, value]pair. It has a forEach but that does NOT returns the callback result.
但我发现它在“函数式”编程中非常有限,因为它缺乏经典的映射、过滤器等方法,可以很好地与[key, value]一对一起工作。它有一个 forEach 但不返回回调结果。
If I could transform its map.entries()from a MapIterator into a simple Array I could then use the standard .map, .filterwith no additional hacks.
如果我可以将它map.entries()从 MapIterator 转换为一个简单的 Array ,那么我就可以使用标准.map,.filter而无需额外的技巧。
Is there a "good" way to transform a Javascript Iterator into an Array?
In python it's as easy as doing list(iterator)... but Array(m.entries())return an array with the Iterator as its first element!!!
有没有一种“好”的方法可以将 Javascript 迭代器转换为数组?在 python 中,它就像做一样简单list(iterator)......但是Array(m.entries())返回一个以迭代器为第一个元素的数组!!!
EDIT
编辑
I forgot to specify I'm looking for an answer which works wherever Map works, which means at least Chrome and Firefox (Array.from does not work in Chrome).
我忘了说明我正在寻找一个适用于 Map 任何地方的答案,这意味着至少是 Chrome 和 Firefox(Array.from 在 Chrome 中不起作用)。
PS.
附注。
I know there's the fantastic wu.jsbut its dependency on traceur puts me off...
回答by Bergi
You are looking for the new Array.fromfunctionwhich converts arbitrary iterables to array instances:
您正在寻找将任意可迭代对象转换为数组实例的新Array.from函数:
var arr = Array.from(map.entries());
It is now supported in Edge, FF, Chrome and Node 4+.
现在支持 Edge、FF、Chrome 和 Node 4+。
Of course, it might be worth to define map, filterand similar methods directly on the iterator interface, so that you can avoid allocating the array. You also might want to use a generator function instead of higher-order functions:
当然,直接在迭代器接口上定义map,filter和类似的方法可能是值得的,这样您就可以避免分配数组。您可能还想使用生成器函数而不是高阶函数:
function* map(iterable) {
var i = 0;
for (var item of iterable)
yield yourTransformation(item, i++);
}
function* filter(iterable) {
var i = 0;
for (var item of iterable)
if (yourPredicate(item, i++))
yield item;
}
回答by Ginden
[...map.entries()]or Array.from(map.entries())
[...map.entries()]或者 Array.from(map.entries())
It's super-easy.
这非常容易。
Anyway - iterators lack reduce, filter, and similar methods. You have to write them on your own, as it's more perfomant than converting Map to array and back. But don't to do jumps Map -> Array -> Map -> Array -> Map -> Array, because it will kill performance.
无论如何 - 迭代器缺少 reduce、filter 和类似的方法。您必须自己编写它们,因为它比将 Map 转换为数组并返回更高效。但是不要做跳转 Map -> Array -> Map -> Array -> Map -> Array,因为这会影响性能。
回答by Aadit M Shah
There's no need to transform a Mapinto an Array. You could simply create mapand filterfunctions for Mapobjects:
无需将 aMap转换为Array。您可以简单地为对象创建map和filter函数Map:
function map(functor, object, self) {
var result = new Map;
object.forEach(function (value, key, object) {
result.set(key, functor.call(this, value, key, object));
}, self);
return result;
}
function filter(predicate, object, self) {
var result = new Map;
object.forEach(function (value, key, object) {
if (predicate.call(this, value, key, object)) result.set(key, value);
}, self);
return result;
}
For example, you could append a bang (i.e. !character) to the value of each entry of a map whose key is a primitive.
例如,您可以将一个 bang(即!字符)附加到键是原始映射的每个条目的值。
var object = new Map;
object.set("", "empty string");
object.set(0, "number zero");
object.set(object, "itself");
var result = map(appendBang, filter(primitive, object));
alert(result.get("")); // empty string!
alert(result.get(0)); // number zero!
alert(result.get(object)); // undefined
function primitive(value, key) {
return isPrimitive(key);
}
function appendBang(value) {
return value + "!";
}
function isPrimitive(value) {
var type = typeof value;
return value === null ||
type !== "object" &&
type !== "function";
}
<script>
function map(functor, object, self) {
var result = new Map;
object.forEach(function (value, key, object) {
result.set(key, functor.call(this, value, key, object));
}, self || null);
return result;
}
function filter(predicate, object, self) {
var result = new Map;
object.forEach(function (value, key, object) {
if (predicate.call(this, value, key, object)) result.set(key, value);
}, self || null);
return result;
}
</script>
You could also add mapand filtermethods on Map.prototypeto make it read better. Although it is generally not advised to modify native prototypes yet I believe that an exception may be made in the case of mapand filterfor Map.prototype:
您还可以添加map和filter方法Map.prototype以使其更好地阅读。尽管通常不建议以修改本机的原型但我认为,一个例外可能的情况下进行map,并filter为Map.prototype:
var object = new Map;
object.set("", "empty string");
object.set(0, "number zero");
object.set(object, "itself");
var result = object.filter(primitive).map(appendBang);
alert(result.get("")); // empty string!
alert(result.get(0)); // number zero!
alert(result.get(object)); // undefined
function primitive(value, key) {
return isPrimitive(key);
}
function appendBang(value) {
return value + "!";
}
function isPrimitive(value) {
var type = typeof value;
return value === null ||
type !== "object" &&
type !== "function";
}
<script>
Map.prototype.map = function (functor, self) {
var result = new Map;
this.forEach(function (value, key, object) {
result.set(key, functor.call(this, value, key, object));
}, self || null);
return result;
};
Map.prototype.filter = function (predicate, self) {
var result = new Map;
this.forEach(function (value, key, object) {
if (predicate.call(this, value, key, object)) result.set(key, value);
}, self || null);
return result;
};
</script>
Edit:In Bergi's answer, he created generic mapand filtergenerator functions for all iterable objects. The advantage of using them is that since they are generator functions, they don't allocate intermediate iterable objects.
编辑:在 Bergi 的回答中,他为所有可迭代对象创建了泛型map和filter生成器函数。使用它们的好处是因为它们是生成器函数,所以它们不分配中间可迭代对象。
For example, my mapand filterfunctions defined above create new Mapobjects. Hence calling object.filter(primitive).map(appendBang)creates two new Mapobjects:
例如,上面定义的mymap和filter函数创建新Map对象。因此调用object.filter(primitive).map(appendBang)会创建两个新Map对象:
var intermediate = object.filter(primitive);
var result = intermediate.map(appendBang);
Creating intermediate iterable objects is expensive. Bergi's generator functions solve this problem. They do not allocate intermediate objects but allow one iterator to feed its values lazily to the next. This kind of optimization is known as fusion or deforestationin functional programming languages and it can significantly improve program performance.
创建中间可迭代对象是昂贵的。Bergi 的生成器函数解决了这个问题。它们不分配中间对象,而是允许一个迭代器将其值懒惰地提供给下一个。这种优化在函数式编程语言中被称为融合或毁林,它可以显着提高程序性能。
The only problem I have with Bergi's generator functions is that they are not specific to Mapobjects. Instead, they are generalized for all iterable objects. Hence instead of calling the callback functions with (value, key)pairs (as I would expect when mapping over a Map), it calls the callback functions with (value, index)pairs. Otherwise, it's an excellent solution and I would definitely recommend using it over the solutions that I provided.
我对 Bergi 的生成器函数的唯一问题是它们不是特定于Map对象的。相反,它们适用于所有可迭代对象。因此,它不是使用(value, key)成对调用回调函数(正如我在映射 a 时所期望的那样Map),而是(value, index)成对调用回调函数。否则,这是一个很好的解决方案,我绝对会推荐使用它而不是我提供的解决方案。
So these are the specific generator functions that I would use for mapping over and filtering Mapobjects:
所以这些是我将用于映射和过滤Map对象的特定生成器函数:
function * map(functor, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
yield [key, functor.call(that, value, key, entries)];
}
}
function * filter(predicate, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
if (predicate.call(that, value, key, entries)) yield [key, value];
}
}
function toMap(entries) {
var result = new Map;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
result.set(key, value);
}
return result;
}
function toArray(entries) {
var array = [];
for (var entry of entries) {
array.push(entry[1]);
}
return array;
}
They can be used as follows:
它们可以按如下方式使用:
var object = new Map;
object.set("", "empty string");
object.set(0, "number zero");
object.set(object, "itself");
var result = toMap(map(appendBang, filter(primitive, object.entries())));
alert(result.get("")); // empty string!
alert(result.get(0)); // number zero!
alert(result.get(object)); // undefined
var array = toArray(map(appendBang, filter(primitive, object.entries())));
alert(JSON.stringify(array, null, 4));
function primitive(value, key) {
return isPrimitive(key);
}
function appendBang(value) {
return value + "!";
}
function isPrimitive(value) {
var type = typeof value;
return value === null ||
type !== "object" &&
type !== "function";
}
<script>
function * map(functor, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
yield [key, functor.call(that, value, key, entries)];
}
}
function * filter(predicate, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
if (predicate.call(that, value, key, entries)) yield [key, value];
}
}
function toMap(entries) {
var result = new Map;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
result.set(key, value);
}
return result;
}
function toArray(entries) {
var array = [];
for (var entry of entries) {
array.push(entry[1]);
}
return array;
}
</script>
If you want a more fluent interface then you could do something like this:
如果你想要一个更流畅的界面,那么你可以这样做:
var object = new Map;
object.set("", "empty string");
object.set(0, "number zero");
object.set(object, "itself");
var result = new MapEntries(object).filter(primitive).map(appendBang).toMap();
alert(result.get("")); // empty string!
alert(result.get(0)); // number zero!
alert(result.get(object)); // undefined
var array = new MapEntries(object).filter(primitive).map(appendBang).toArray();
alert(JSON.stringify(array, null, 4));
function primitive(value, key) {
return isPrimitive(key);
}
function appendBang(value) {
return value + "!";
}
function isPrimitive(value) {
var type = typeof value;
return value === null ||
type !== "object" &&
type !== "function";
}
<script>
MapEntries.prototype = {
constructor: MapEntries,
map: function (functor, self) {
return new MapEntries(map(functor, this.entries, self), true);
},
filter: function (predicate, self) {
return new MapEntries(filter(predicate, this.entries, self), true);
},
toMap: function () {
return toMap(this.entries);
},
toArray: function () {
return toArray(this.entries);
}
};
function MapEntries(map, entries) {
this.entries = entries ? map : map.entries();
}
function * map(functor, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
yield [key, functor.call(that, value, key, entries)];
}
}
function * filter(predicate, entries, self) {
var that = self || null;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
if (predicate.call(that, value, key, entries)) yield [key, value];
}
}
function toMap(entries) {
var result = new Map;
for (var entry of entries) {
var key = entry[0];
var value = entry[1];
result.set(key, value);
}
return result;
}
function toArray(entries) {
var array = [];
for (var entry of entries) {
array.push(entry[1]);
}
return array;
}
</script>
Hope that helps.
希望有帮助。
回答by nromaniv
A small update from 2019:
2019 年的一个小更新:
Now Array.from seems to be universally available, and, furthermore, it accepts a second argument mapFn, which prevents it from creating an intermediate array. This basically looks like this:
现在 Array.from 似乎是普遍可用的,而且,它接受第二个参数mapFn,这可以防止它创建一个中间数组。这基本上是这样的:
Array.from(myMap.entries(), entry => {...});
回答by dimadeveatii
You could use a library like https://www.npmjs.com/package/itiririthat implements array-like methods for iterables:
您可以使用像https://www.npmjs.com/package/itiriri这样的库,它为可迭代对象实现类似数组的方法:
import { query } from 'itiriri';
const map = new Map();
map.set(1, 'Alice');
map.set(2, 'Bob');
const result = query(map)
.filter([k, v] => v.indexOf('A') >= 0)
.map([k, v] => `k - ${v.toUpperCase()}`);
for (const r of result) {
console.log(r); // prints: 1 - ALICE
}
回答by ValRob
You can get the array of arrays(key and value):
您可以获得数组的数组(键和值):
[...this.state.selected.entries()]
/**
*(2) [Array(2), Array(2)]
*0: (2) [2, true]
*1: (2) [3, true]
*length: 2
*/
And then, you can easly get values from inside, like for example the keys with the map iterator.
然后,您可以轻松地从内部获取值,例如带有映射迭代器的键。
[...this.state.selected[asd].entries()].map(e=>e[0])
//(2)?[2, 3]
回答by kataik
You can also use fluent-iterableto transform to array:
您还可以使用fluent-iterable转换为数组:
const iterable: Iterable<T> = ...;
const arr: T[] = fluent(iterable).toArray();

