Skip to content

Instantly share code, notes, and snippets.

@romellem
Created March 21, 2023 19:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save romellem/56b23de24eeed3874498a418b0786968 to your computer and use it in GitHub Desktop.
Save romellem/56b23de24eeed3874498a418b0786968 to your computer and use it in GitHub Desktop.
Extends native JS `Map` class to allow for default values when a `key` does not exist on the map
class MapWithDefaultValues extends Map {
constructor(...args) {
super(...args);
this._defaultGetValue = undefined;
}
setDefault(value) {
this._defaultGetValue = value;
return this;
}
clearDefault() {
return this.setDefault(undefined);
}
get(key, ...rest) {
if (super.has(key)) {
return super.get(key);
}
let [defaultValue] = rest;
// Allow for `get(key, undefined)` to override `this._defaultGetValue`
if (rest.length === 0) {
defaultValue = this._defaultGetValue;
}
return typeof defaultValue === 'function' ? defaultValue(key, this) : defaultValue;
}
}
const assert = require('assert');
let mapDefault = new MapWithDefaultValues();
mapDefault.setDefault('default');
mapDefault.set('a', 1);
assert.strictEqual(mapDefault.get('a'), 1);
assert.strictEqual(mapDefault.get('a', 'otherDefault'), 1);
assert.strictEqual(mapDefault.get('none'), 'default');
assert.strictEqual(mapDefault.get('alsoNone', 'override'), 'override');
// When a function is passed as the default, it is called with `(key, map)` as its 1st and 2nd arguments,
// and that function's return value is used as the default.
let mapDefaultFunc = new MapWithDefaultValues();
mapDefaultFunc.setDefault(key => {
return typeof key === 'number' ? key * 2 : 'default';
});
assert.strictEqual(mapDefaultFunc.get('b'), 'default');
assert.strictEqual(mapDefaultFunc.get(5), 10); // 5 * 2
assert.strictEqual(
mapDefaultFunc.get('none', key => {
const keyLength = key?.length ?? 0;
return keyLength * 10;
}),
40
); // 'none'.length * 10
mapDefaultFunc.set('x', -1);
mapDefaultFunc.set('y', -2);
assert.strictEqual(
mapDefaultFunc.get('alsoNone', (key, map) => {
return map.size;
}),
2
);
// If you want a defaultValue that is itself a function,
// you'll need to pass a function *that returns* a function,
// since function defaultValues are evaluated first.
let funcsMap = new MapWithDefaultValues();
funcsMap.setDefault(() => {
return function identity(val) {
return val;
};
});
funcsMap.set('double', v => v * 2);
funcsMap.set('triple', v => v * 3);
let fn = funcsMap.get('double');
assert.strictEqual(fn(3), 6);
fn = funcsMap.get('quad');
assert.strictEqual(fn(3), 3); // Uses default `identity` since 'quad' does not exist
fn = funcsMap.get('negate', () => {
return function negate(v) {
return -v;
};
});
assert.strictEqual(fn(3), -3);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment