Feature #19148
openDefine Hash#merge_sum as Hash#merge with sum block
Description
To merge two hashes whilst summing any common elements, we currently have to pass a block instructing how to add the elements from each hash:
items_1_counts = {:book => 4, :pen => 2, :pencil => 3}
items_2_counts = {:book => 2, :pencil => 6, :rubber => 11}
items_1_counts.merge(items_2_counts) { |_k, a, b| a + b }
=> {:book => 6, :pen => 2, :pencil => 9, :rubber => 11}
Having a method for this would (in my opinion) simplify it:
items_1_counts.merge_sum(items_2_counts)
=> { :book => 6, :pen => 2, :pencil => 9, :rubber => 11 }
Would this benefit many people?¶
This (merging two hashes whilst summing any common elements) seems to be a common scenario that many people raise on forums:
Updated by janosch-x (Janosch Müller) almost 2 years ago
naming, edge cases¶
merge_sum
would be a confusing name IMO, because the method neither takes nor returns a (single) sum.
i'd suggest merge_tally
, because it is in the same problem domain as Enumerable#tally
.
merge_tally
could then raise the same TypeErrors as tally
in cases such as
{ a: 7 }.merge_tally(a: 'string')
other option¶
not sure about this, but maybe Hash
could have a custom implementation of tally
.
Ruby 3.1 introduced the option to pass an accumulator to tally
:
hash = {}
hash = %w[a c d b c a].tally(hash)
hash # => {"a"=>2, "c"=>2, "d"=>1, "b"=>1}
hash = %w[b a z].tally(hash)
hash # => {"a"=>3, "c"=>2, "d"=>1, "b"=>2, "z"=>1}
this does not yet work for merging tallies because:
{ a: 7 }.tally # => {[:a, 7]=>1}
{ a: 7 }.tally(a: 3) # => {:a=>3, [:a, 7]=>1}
however, a Hash#tally
override could make it act like this:
{ a: 7 }.tally # => {:a=>7}
{ a: 7 }.tally(a: 3) # => {:a=>10}
while that would be a breaking change, the current behavior that maps all KV pairs to 1 is a bit pointless and probably not consciously used by anyone?