Feature #20818
openAllow passing a block to Hash#store (to update current value)
Description
I would like to propose a block form for Hash#store
. In addition to passing a value, it should also be allowed to pass a block.
If passed a block instead of a value, the block is called with the current value or, if unset, the hash's default value; the block's return value will be the value that is stored.
This is similar to e.g., computeIfPresent
/computeIfAbsent
in Java (I think).
I can think of several situations where this would be useful, in particular for caches and counters.
For instance:
counts = {}
elements.each do |element|
counts.store(element){ (_1 || 0) + element.weight}
end
or even more elegant with a default value:
counts = {}
counts.default = 0
elements.each do |element|
counts.store(element){ _1 + element.weight}
end
Moreover, using the block form we should be able to do operations such as h[k] ||= x
, h[k] -= x
, h[k] += x
, or more generally h[k] = f(h[k])
, with a single "hashing round-trip".
If I'm not mistaken, currently these involve two separate calls to #[]
and #[]=
(with two calls to #hash
?).
Finally, this makes #store
a proper dual of #fetch
which, similarly, can be passed a block.
I have an experimental implementation of this (GitHub PR) at: https://github.com/ruby/ruby/pull/11956