When working with Javascript objects, we have access to a large variety of operators, such as obj.x++
, obj.x ??= 2
, or obj.x **= 2
to get the job done. It's unfortunately not possible to use operators such as those when accessing values from an instances of Map, WeakMap, etc. This leads to highly unreadable code to do basic operations:
// Increment a value
map.set(map.get('myKey') + 1)
// Set a non-existent value
if (!map.has('myKey')) {
map.set('defaultValue')
}
This has caused us to start looking into creating awkward convenience functions to do what's already possible to do with these operators, like the proposed emplace() function here which attempts to solve the past two scenarios as follows:
// Increment a value
counts.emplace('myKey', {
update: existing => existing + 1
});
// Set a non-existent value
map.emplace('myKey', {
insert: () => 'defaultValue'
});
I propose we add a .getRef()
method to Map, Set, and any other data structure that could benefit from it. It will return a reference with a "value" getter/setter property that can be used to update that specific entry. It can be defined somewhat like this:
// Note that browsers can add a bit of caching,
// so a lookup doesn't have to happen with each ref operation.
Map.prototype.getRef = function(key) {
const self = this
return {
get value() {
return self.get(key)
},
set value(newVal) {
self.set(key, newVal)
},
exists() {
return self.has(key)
},
}
}
Now we can write our previous example in a very simple, straight-forward way:
// Increment a value
map.getRef('myKey').value++
// Set a non-existent value
map.getRef('myKey').value ??= 'defaultValue'
Some other motivating examples:
// Insert 0 if doesn't exist, then increment
const ref = map.getRef(key)
(ref.value ??= 0)++
// Another insert or update example
const ref = map.getRef(computeKey())
ref.value = !ref.exists() ? createNew : updateValue(ref.value)