在迭代器上使用map()

shi*_*zou 64 javascript syntax dictionary iterator ecmascript-6

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

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

那么有没有办法map()在迭代器上使用?

kti*_*lcu 57

执行此操作的最简单且性能最差的方法是:Array.from(m).map(([key,value])=> //无论如何)

Array.from 采取任何可迭代或类似数组的东西,并给它阵列的权力!

然而,正如@hraban在评论中指出的那样,这将把你的表现从O(1)移到O(n).既然Array.from是一个O(1),O(n)也不可能是无限的,我们不必担心无限的序列.对于大多数情况,这就足够了.

还有其他几种方法可以遍历地图.

m

Array.from(m).map(([key,value]) => /* whatever */)
Run Code Online (Sandbox Code Playgroud)

Map

Array.from(m, ([key, value]) => /* whatever */))
Run Code Online (Sandbox Code Playgroud)

  • 这个答案的问题在于它可以将O(1)内存算法转换为O(n)内存算法,这对于较大的数据集来说非常严重.当然,还需要有限的,不可流动的迭代器.问题的标题是"在迭代器上使用map()",我不同意懒惰和无限序列不是问题的一部分.这正是人们使用迭代器的方式."地图"只是一个例子("Say ..").这个答案的好处是简单,这非常重要. (5认同)
  • 如果m具有无限长,将不起作用。 (2认同)
  • @ktilcu for a iterator:是的.迭代器上的.map可以被认为是生成器的变换,它返回一个迭代器本身.弹出一个元素调用底层迭代器,转换元素,然后返回它. (2认同)
  • @hraban 感谢您加入此讨论。我可以更新答案以包含一些注意事项,以便未来的旅行者能够获得最重要的信息。归根结底,我们通常必须在简单性能和最佳性能之间做出决定。我通常会倾向于更简单的(调试、维护、解释)而不是性能。 (2认同)
  • @ktilcu您可以改为调用Array.from(m,([[key,value])=> / *任意* /)`(注意映射函数位于`from`内部),然后不会创建任何中间数组([来源](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Description))。它仍然从O(1)移到O(n),但是至少迭代和映射仅发生在一个完整的迭代中。 (2认同)

小智 17

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

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

function* mapIterator(iterator, mapping) {
    while (true) {
        let result = iterator.next();
        if (result.done) {
            break;
        }
        yield mapping(result.value);
    }
}

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

现在你可能会问:为什么不Array.from改用呢?因为这将遍历整个迭代器,将其保存到(临时)数组,再次迭代它然后执行映射.如果列表很大(甚至可能是无限的),这将导致不必要的内存使用.

当然,如果项目列表相当小,使用Array.from应该绰绰有余.

  • 它没有,这就是重点.使用此方法,您可以通过将迭代器源链接到一堆迭代器转换以及最终消费者接收器来创建"数据流".例如,用于流式音频处理,使用大型文件,数据库聚合器等. (3认同)

Ian*_*lor 12

这种最简单、最高效的方法是使用第二个参数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']
Run Code Online (Sandbox Code Playgroud)

这种方法适用于任何非无限迭代。并且它避免了必须使用单独的调用,Array.from(map).map(...)该调用会遍历可迭代对象两次并且性能更差。


chp*_*pio 10

有一个提案,将多个辅助函数引入Iteratorhttps://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);
Run Code Online (Sandbox Code Playgroud)


cow*_*cks 5

这里的其他答案是......奇怪。他们似乎正在重新实施迭代协议的部分内容。你可以这样做:

function* mapIter(iterable, callback) {
  for (let x of iterable) {
    yield callback(x);
  }
}
Run Code Online (Sandbox Code Playgroud)

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

[...iterMap([1, 2, 3], x => x**2)]
Run Code Online (Sandbox Code Playgroud)