5

Given two hashes whose values are arrays, what is the best way to merge them so that when the two shares some key, the resulting value will be the concatenation of the values of the original two hashes? For example, given two hashes h1 and h2:

h1 = Hash.new{[]}.merge(a: [1], b: [2, 3])
h2 = Hash.new{[]}.merge(b: [4], c: [5])

I expect that the method convolute will give:

h1.convolute(h2) #=> {:a => [1], b: [2, 3, 4], c: [5]}
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
sawa
  • 165,429
  • 45
  • 277
  • 381

2 Answers2

16

This is exactly what Hash#merge does if you give it a block:

h1.merge(h2) do |key, v1, v2|
  v1 + v2
end

http://rubydoc.info/stdlib/core/1.9.2/Hash:merge

Theo
  • 131,503
  • 21
  • 160
  • 205
  • 2
    +1. Maybe `<<` in stead of `+` is even better, not creating a new array. – steenslag Mar 19 '12 at 10:05
  • @steenslag But both v1 and v2 are arrays. – sawa Mar 19 '12 at 17:26
  • @sawa Yes, but v1 + v2 creates a new (third) array. << keeps the total at two. Same thing with strings btw. – steenslag Mar 19 '12 at 17:38
  • 4
    I think you mean `v1.concat(v2)`, `v1 << v2` would add `v2` as the last element of `v1`, not concatenate. In other words `v1 + v2` and `v1.concat(v2)` are equivalent except that the latter mutates `v1`, whereas `v1 << v2` would give you something like `[2, 3, [4]]` in the case of the `b` key from the example in the question. I assumed, however, the OP didn't want to mutate his/her datastructures. There's definitely a place for optimizations like not creating extra unnecessary objects, but it's also a common source for bugs. – Theo Mar 20 '12 at 15:12
2

If you don't care about modifying h2 then:

h1.each_with_object(h2) { |(k, v), h| h[k] += v }

If you want to leave h2 alone:

h1.each_with_object(h2.dup) { |(k, v), h| h[k] += v }

And if you want that specific order:

h2.each_with_object(h1.dup) { |(k, v), h| h[k] += v }
mu is too short
  • 426,620
  • 70
  • 833
  • 800