I'm working on a project where I frequently have to transform every value in an ES6 map:
const positiveMap = new Map(
[
['hello', 1],
['world', 2]
]
);
const negativeMap = new Map<string, number>();
for (const key of positiveMap.keys()) {
negativeMap.set(key, positiveMap.get(key) * -1);
}
Just wondering if there is maybe a better way of doing this? Ideally a one liner like Array.map()
.
Bonus points (not really), if it piles in typescript!
I'm working on a project where I frequently have to transform every value in an ES6 map:
const positiveMap = new Map(
[
['hello', 1],
['world', 2]
]
);
const negativeMap = new Map<string, number>();
for (const key of positiveMap.keys()) {
negativeMap.set(key, positiveMap.get(key) * -1);
}
Just wondering if there is maybe a better way of doing this? Ideally a one liner like Array.map()
.
Bonus points (not really), if it piles in typescript!
Share Improve this question edited Feb 1, 2018 at 20:30 NSjonas asked Feb 1, 2018 at 20:13 NSjonasNSjonas 12k11 gold badges75 silver badges101 bronze badges 3-
2
@AluanHaddad
.entries()
returns an iterator, not an array of key/value pairs. That won't work. – Patrick Roberts Commented Feb 1, 2018 at 20:17 - @PatrickRoberts you're right. (hits head with hand) – Aluan Haddad Commented Feb 1, 2018 at 20:18
-
for anyone that cares (about microbenchmarks) copying manually using
.forEach
is an order of magnitude faster on V8. Mutating in-place is another order of magnitude faster. Not that it will actually really matter – user120242 Commented Jun 6, 2020 at 21:50
4 Answers
Reset to default 9You could use the Array.from
2nd argument, a map-style callback:
const positiveMap = new Map([['hello', 1],['world', 2]]),
negativeMap = new Map(Array.from(positiveMap, ([k, v]) => [k, -v]));
console.log([...negativeMap]);
You could transform it into array using spread syntax ...
, apply map()
method and then again transform it to Map
const positiveMap = new Map([['hello', 1],['world', 2]]);
const negativeMap = new Map([...positiveMap].map(([k, v]) => [k, v * -1]))
console.log([...negativeMap])
If you want, you can extend Map
with your own class and include functionality to generically iterate it like an array:
class ArrayMap extends Map {
map (fn, thisArg) {
const { constructor: Map } = this;
const map = new Map();
for (const [key, value] of this.entries()) {
map.set(key, fn.call(thisArg, value, key, this));
}
return map;
}
forEach (fn, thisArg) {
for (const [key, value] of this.entries()) {
fn.call(thisArg, value, key, this);
}
}
reduce (fn, accumulator) {
const iterator = this.entries();
if (arguments.length < 2) {
if (this.size === 0) throw new TypeError('Reduce of empty map with no initial value');
accumulator = iterator.next().value[1];
}
for (const [key, value] of iterator) {
accumulator = fn(accumulator, value, key, this);
}
return accumulator;
}
every (fn, thisArg) {
for (const [key, value] of this.entries()) {
if (!fn.call(thisArg, value, key, this)) return false;
}
return true;
}
some (fn, thisArg) {
for (const [key, value] of this.entries()) {
if (fn.call(thisArg, value, key, this)) return true;
}
return false;
}
// ...
}
const positiveMap = new ArrayMap(
[
['hello', 1],
['world', 2]
]
);
const negativeMap = positiveMap.map(value => -value);
negativeMap.forEach((value, key) => console.log(key, value));
I threw in reduce()
, every()
and some()
for free. Implement as many or as few of the methods you like or need.
You could have a generic typescript function based on trincot's answer
function transformMap<K, V, U>(source: Map<K, V>, func: (key: K, value: V) => U): Map<K, U> {
return new Map(Array.from(source, (v) => [v[0], func(v[0], v[1])]));
}
and use it like this
transformMap(positiveMap, (key, value) => -value)