Default Map in JavaScript
snippetlet index = new DefaultMap(() => []);
// …
for(let category of ["good", "bad", "ugly"]) {
let values = index.get(category);
values.push(/* … */);
}
That’s often much cleaner than using a regular Map
and branching inline
(because separation of concerns, plus it’s easy to make subtle mistakes):
let index = new Map();
// …
for(let category of ["good", "bad", "ugly"]) {
let values = index.get(category);
if(values === undefined) {
values = [];
index.set(category, values);
}
values.push(/* … */);
}
My implementation always ends up looking like this (reluctantly augmented with static types here, just in case):
/**
* @template Key
* @template Value
* @extends Map<Key, Value>
*/
class DefaultMap extends Map {
#initializer;
/** @param {(key: Key) => Value} initializer */
constructor(initializer) {
super();
this.#initializer = initializer;
}
/**
* @param {Key} key
* @returns {Value}
*/
get(key) {
if(this.has(key)) {
return /** @type {Value} */ (super.get(key));
}
let value = this.#initializer(key);
this.set(key, value);
return value;
}
}