在迭代器上使用 map()

假设我们有一个 Map: let m = new Map();,使用 m.values()返回一个 Map 迭代器。

但是我不能在那个迭代器上使用 forEach()map(),并且在那个迭代器上实现 while 循环看起来像是反模式,因为 ES6提供了类似于 map()的函数。

那么有没有一种方法可以在迭代器上使用 map()呢?

85988 次浏览

最简单的表现最差做到这一点的方法是:

Array.from(m).map(([key,value]) => /* whatever */)

还有更好的

Array.from(m, ([key, value]) => /* whatever */))

Array.from 接受任何可迭代的或类似数组的内容并将其转换为数组!正如 Daniel 在注释中指出的,我们可以在转换中添加一个映射函数,以删除一个迭代,然后删除一个中间数组。

正如@hraban 在评论中指出的那样,使用 Array.from将使你的性能从 O(1)提升到 O(n)。因为 mMap,它们不可能是无限的,所以我们不必担心无限序列。对于大多数情况,这就足够了。

还有其他一些方法可以循环遍历地图。

使用 forEach

m.forEach((value,key) => /* stuff */ )

使用 for..of

var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one

您可以定义另一个迭代器函数来循环这个:

function* generator() {
for (let i = 0; i < 10; i++) {
console.log(i);
yield i;
}
}


function* mapIterator(iterator, mapping) {
for (let i of iterator) {
yield mapping(i);
}
}


let values = generator();
let mapped = mapIterator(values, (i) => {
let result = i*2;
console.log(`x2 = ${result}`);
return result;
});


console.log('The values will be generated right now.');
console.log(Array.from(mapped).join(','));

现在您可能会问: 为什么不直接使用 Array.from呢?因为这将贯穿整个迭代器,所以将它保存到一个(临时)数组中,再次迭代它,然后由 那么执行映射。如果列表很大(甚至可能是无限的) ,这将导致不必要的内存使用。

当然,如果项目列表相当小,使用 Array.from应该就足够了。

您可以使用 Tiriri为迭代实现类似数组的方法:

import { query } from 'itiriri';


let m = new Map();
// set map ...


query(m).filter([k, v] => k < 10).forEach([k, v] => console.log(v));
let arr = query(m.values()).map(v => v * 10).toArray();

您可以在迭代器上检索一个迭代器,然后返回另一个迭代器,该迭代器调用每个迭代元素上的映射回调函数。

const map = (iterable, callback) => {
return {
[Symbol.iterator]() {
const iterator = iterable[Symbol.iterator]();
return {
next() {
const r = iterator.next();
if (r.done)
return r;
else {
return {
value: callback(r.value),
done: false,
};
}
}
}
}
}
};


// Arrays are iterable
console.log(...map([0, 1, 2, 3, 4], (num) => 2 * num)); // 0 2 4 6 8

这种最简单、性能最好的方法是使用 Array.from的第二个参数来实现这一点:

const map = new Map()
map.set('a', 1)
map.set('b', 2)


Array.from(map, ([key, value]) => `${key}:${value}`)
// ['a:1', 'b:2']

这种方法适用于任何 非无限可迭代文件。而且它避免了使用对 Array.from(map).map(...)的单独调用,这种调用将迭代遍历迭代器两次,并且会对性能造成更坏的影响。

看看 https://www.npmjs.com/package/fluent-iterable

适用于所有的迭代器(地图、生成器函数、数组)和异步迭代器。

const map = new Map();
...
console.log(fluent(map).filter(..).map(..));

这里的其他答案是... ... 奇怪的。它们似乎是重新实现迭代协议的一部分。你可以这样做:

function* mapIter(iterable, callback) {
for (let x of iterable) {
yield callback(x);
}
}

如果你想要一个具体的结果,只需使用展开运算符 ...

[...mapIter([1, 2, 3], x => x**2)]

有一个提议,将多个助手函数引入到 Iterator: https://github.com/tc39/proposal-iterator-helpers(呈现)

你今天可以通过使用 core-js-pure来使用它:

import { from as iterFrom } from "core-js-pure/features/iterator";


// or if it's working for you (it should work according to the docs,
// but hasn't for me for some reason):
// import iterFrom from "core-js-pure/features/iterator/from";


let m = new Map();


m.set("13", 37);
m.set("42", 42);


const arr = iterFrom(m.values())
.map((val) => val * 2)
.toArray();


// prints "[74, 84]"
console.log(arr);

基于 MartyO256(https://stackoverflow.com/a/53159921/7895659)的答案,一种重构的类型脚本方法可以是下面这种方法:

function mapIterator<TIn, TOut>(
iterator: Iterator<TIn>,
callback: (input: TIn) => TOut,
): Iterator<TOut> {
return {
next() {
const result: IteratorResult<TIn> = iterator.next();


if (result.done === true) {
return result;
} else {
return {
done: false,
value: callback(result.value),
};
}
},
};
}


export function mapIterable<TIn, TOut>(
iterable: Iterable<TIn>,
callback: (input: TIn) => TOut,
): Iterable<TOut> {
const iterator: Iterator<TIn> = iterable[Symbol.iterator]();
const mappedIterator: Iterator<TOut> = mapIterator(iterator, callback);


return {
[Symbol.iterator]: () => mappedIterator,
};
}

如果有人需要打印稿的话:

function* mapIter<T1, T2>(iterable: IterableIterator<T1>, callback: (value: T1) => T2) {
for (let x of iterable) {
yield callback(x);
}
}