如何在不扩展到列表的情况下减少(...) JavaScript Map 对象的条目?
How can you reduce(...) the entries of a JavaScript Map object without expanding to a list?
似乎没有好的标准库方法来做这样的事情?
let thismap = new Map([[1,2],[2,3]])
console.log(thismap.entries().reduce((prev, [a,b])=>prev + a * b, 0))
Uncaught TypeError: thismap.entries(...).reduce is not a function
我假设这是由于 entries() 函数返回了一个迭代器?我不想 Array.from(thismap.entries()).reduce(...)
,因为那样会不必要地在内存中构建数组。感觉好像遗漏了一些东西,但我也不想重新实现标准库中应该有的东西。
我想如果我改用一个对象(由于其他原因这里不是一个令人满意的解决方案),entries() 本质上将是一个数组扩展而不是迭代器(尽管我认为它可以通过内存效率来实现心里)。但是,我仍然想知道如何减少迭代器
您可以使用 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)
当然,您也可以将其分解为效用函数,以备不时之需。在这里,我创建了一个函数 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))
如果你愿意,你可以扩展 Map
、Set
等的原型,即 Map.prototype.reduce = function (...args) { return lazyReduce(this, ...args) }
。 (注意:return 迭代器的其他一些东西将更难扩展,但仍然是可能的。例如 RegExpStringIterator
不作为全局变量存在,但你仍然可以做 Object.getPrototypeOf(''.matchAll(/./g)).reduce = ...
。类似的想法适用于 Generator
。)
I also don't want to reimplement something that should be in the standard library.
确实应该。有一个添加它的建议:Iterator Helpers. While waiting for it, you can already use the polyfill 这将使您的原始代码工作:-)
似乎没有好的标准库方法来做这样的事情?
let thismap = new Map([[1,2],[2,3]])
console.log(thismap.entries().reduce((prev, [a,b])=>prev + a * b, 0))
Uncaught TypeError: thismap.entries(...).reduce is not a function
我假设这是由于 entries() 函数返回了一个迭代器?我不想 Array.from(thismap.entries()).reduce(...)
,因为那样会不必要地在内存中构建数组。感觉好像遗漏了一些东西,但我也不想重新实现标准库中应该有的东西。
我想如果我改用一个对象(由于其他原因这里不是一个令人满意的解决方案),entries() 本质上将是一个数组扩展而不是迭代器(尽管我认为它可以通过内存效率来实现心里)。但是,我仍然想知道如何减少迭代器
您可以使用 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)
当然,您也可以将其分解为效用函数,以备不时之需。在这里,我创建了一个函数 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))
如果你愿意,你可以扩展 Map
、Set
等的原型,即 Map.prototype.reduce = function (...args) { return lazyReduce(this, ...args) }
。 (注意:return 迭代器的其他一些东西将更难扩展,但仍然是可能的。例如 RegExpStringIterator
不作为全局变量存在,但你仍然可以做 Object.getPrototypeOf(''.matchAll(/./g)).reduce = ...
。类似的想法适用于 Generator
。)
I also don't want to reimplement something that should be in the standard library.
确实应该。有一个添加它的建议:Iterator Helpers. While waiting for it, you can already use the polyfill 这将使您的原始代码工作:-)