Created
March 21, 2023 19:08
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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