如何在不扩展为列表的情况下减少(...) JavaScript Map 对象的条目?

bru*_*sby 9 javascript dictionary functional-programming

似乎没有好的标准库方法来做这样的事情?

let thismap = new Map([[1,2],[2,3]])
console.log(thismap.entries().reduce((prev, [a,b])=>prev + a * b, 0))
Run Code Online (Sandbox Code Playgroud)

Uncaught TypeError: thismap.entries(...).reduce is not a function

我认为这是由于 Entry() 函数返回迭代器所致?我不想这样做Array.from(thismap.entries()).reduce(...),因为这会不必要地在内存中构建数组。感觉好像我错过了一些东西,但我也不想重新实现应该在标准库中的东西。

我想如果我使用一个对象来代替(由于其他原因,这里不是一个令人满意的解决方案),entries() 本质上将是一个数组扩展而不是迭代器(尽管我认为它可以在考虑到内存效率的情况下实现)。但我仍然想知道如何减少迭代器

Ber*_*rgi 6

我也不想重新实现标准库中应该包含的东西。

确实应该如此。有一个添加它的建议:Iterator Helpers。在等待的过程中,您已经可以使用polyfill,这将使您的原始代码正常工作:-)

  • @brubsby 不幸的是,事实并非如此。有些提案搁置多年直到最终被接受,而另一些提案则进展得更快。 (2认同)

Che*_*yDT 5

您可以使用for of循环并手动处理求和。这使用迭代器而不创建临时数组。请注意,这里我们甚至不必entries手动调用,因为Map.prototype[Symbol.iterator] === Map.prototype.entries.

const map = new Map([[1, 2], [2, 3]])

let sum = 0
for (const [a, b] of map) sum += a * b

console.log(sum)
Run Code Online (Sandbox Code Playgroud)

当然,您也可以将其分解为实用函数,以防您更频繁地需要它。在这里,我创建了一个函数lazyReduce,其工作方式类似于Array.prototype.reduce但可对任何类型的可迭代对象进行操作:

function lazyReduce (originalIterable, callback, initialValue) {
  let i = 0
  let accumulator = initialValue
  let iterable = originalIterable
  
  // This part exists to implement the behavior of reduce without initial value
  // in the same way Array.prototype.reduce does it
  if (arguments.length < 3) {
    iterable = iterable[Symbol.iterator]()
    const { value, done } = iterable.next()
    if (done) throw new TypeError('Reduce of empty iterable with no initial value')
    accumulator = value
    i++
  }
  
  for (const element of iterable) {
    accumulator = callback(accumulator, element, i++, originalIterable)
  }
  
  return accumulator
}

const map = new Map([[1, 2], [2, 3]])

console.log(lazyReduce(map, (prev, [a, b]) => prev + a * b, 0))
Run Code Online (Sandbox Code Playgroud)

Map如果您愿意,您可以扩展、等的原型Set,即Map.prototype.reduce = function (...args) { return lazyReduce(this, ...args) }。(注意:返回迭代器的其他一些东西将更难扩展,但仍然是可能的。例如,RegExpStringIterator它不作为全局变量存在,但您仍然可以这样做Object.getPrototypeOf(''.matchAll(/./g)).reduce = ...。类似的想法也适用于Generator。)