title: How to merge array of hashes to get hash of arrays of values
body: <p>This is the opposite of <a href="https://stackoverflow.com/questions/1640979/turning-a-hash-of-arrays-into-an-array-of-hashes-in-ruby">Turning a Hash of Arrays into an Array of Hashes in Ruby</a>.</p>
  
  <p>Elegantly and/or efficiently turn an array of hashes into a hash where the values are arrays of all values:</p>
  
  <pre><code>hs = [
    { a:1, b:2 },
    { a:3, c:4 },
    { b:5, d:6 }
  ]
  collect_values( hs )
  #=&gt; { :a=&gt;[1,3], :b=&gt;[2,5], :c=&gt;[4], :d=&gt;[6] }
  </code></pre>
  
  <p>This terse code almost works, but fails to create an array when there are no duplicates:</p>
  
  <pre><code>def collect_values( hashes )
    hashes.inject({}){ |a,b| a.merge(b){ |_,x,y| [*x,*y] } }
  end
  collect_values( hs )
  #=&gt; { :a=&gt;[1,3], :b=&gt;[2,5], :c=&gt;4, :d=&gt;6 }
  </code></pre>
  
  <p>This code works, but can you write a better version?</p>
  
  <pre><code>def collect_values( hashes )
    # Requires Ruby 1.8.7+ for Object#tap
    Hash.new{ |h,k| h[k]=[] }.tap do |result|
      hashes.each{ |h| h.each{ |k,v| result[k]&lt;&lt;v } }
    end
  end
  </code></pre>
  
  <p><em>Solutions that only work in Ruby 1.9 are acceptable, but should be noted as such.</em></p>
  
  <hr>
  
  <p>Here are the results of benchmarking the various answers below (and a few more of my own), using three different arrays of hashes:</p>
  
  <ul>
  <li><p>one where each hash has distinct keys, so no merging ever occurs:<br>
  <code>[{:a=&gt;1}, {:b=&gt;2}, {:c=&gt;3}, {:d=&gt;4}, {:e=&gt;5}, {:f=&gt;6}, {:g=&gt;7}, ...]</code></p></li>
  <li><p>one where every hash has the same key, so maximum merging occurs:<br>
  <code>[{:a=&gt;1}, {:a=&gt;2}, {:a=&gt;3}, {:a=&gt;4}, {:a=&gt;5}, {:a=&gt;6}, {:a=&gt;7}, ...]</code></p></li>
  <li>and one that is a mix of unique and shared keys:<br>
  <code>[{:c=&gt;1}, {:d=&gt;1}, {:c=&gt;2}, {:f=&gt;1}, {:c=&gt;1, :d=&gt;1}, {:h=&gt;1}, {:c=&gt;3}, ...]</code></li>
  </ul>
  
  <pre>
                 user     system      total        real
  Phrogz 2a  0.577000   0.000000   0.577000 (  0.576000)
  Phrogz 2b  0.624000   0.000000   0.624000 (  0.620000)
  Glenn 1    0.640000   0.000000   0.640000 (  0.641000)
  Phrogz 1   0.671000   0.000000   0.671000 (  0.668000)
  Michael 1  0.702000   0.000000   0.702000 (  0.700000)
  Michael 2  0.717000   0.000000   0.717000 (  0.726000)
  Glenn 2    0.765000   0.000000   0.765000 (  0.764000)
  fl00r      0.827000   0.000000   0.827000 (  0.836000)
  sawa       0.874000   0.000000   0.874000 (  0.868000)
  Tokland 1  0.873000   0.000000   0.873000 (  0.876000)
  Tokland 2  1.077000   0.000000   1.077000 (  1.073000)
  Phrogz 3   2.106000   0.093000   2.199000 (  2.209000)
  </pre>
  
  <p>The fastest code is this method that I added:</p>
  
  <pre><code>def collect_values(hashes)
    {}.tap{ |r| hashes.each{ |h| h.each{ |k,v| (r[k]||=[]) &lt;&lt; v } } }
  end
  </code></pre>
  
  <p>I've accepted "<a href="https://stackoverflow.com/questions/5490952/merge-array-of-hashes-to-get-hash-of-arrays-of-values/5491741#5491741">glenn mcdonald's answer</a>" as it was competitive in terms of speed, reasonably terse, but (most importantly) because it pointed out the danger of using a Hash with a self-modifying default proc for convenient construction, as this may introduce bad changes when the user is indexing it later on.</p>
  
  <p>Finally, here's the benchmark code, in case you want to run your own comparisons:</p>
  
  <pre><code>require 'prime'   # To generate the third hash
  require 'facets'  # For tokland1's map_by
  AZSYMBOLS = (:a..:z).to_a
  TESTS = {
    '26 Distinct Hashes'   =&gt; AZSYMBOLS.zip(1..26).map{|a| Hash[*a] },
    '26 Same-Key Hashes'   =&gt; ([:a]*26).zip(1..26).map{|a| Hash[*a] },
    '26 Mixed-Keys Hashes' =&gt; (2..27).map do |i|
      factors = i.prime_division.transpose
      Hash[AZSYMBOLS.values_at(*factors.first).zip(factors.last)]
    end
  }
  
  def phrogz1(hashes)
    Hash.new{ |h,k| h[k]=[] }.tap do |result|
      hashes.each{ |h| h.each{ |k,v| result[k]&lt;&lt;v } }
    end
  end
  def phrogz2a(hashes)
    {}.tap{ |r| hashes.each{ |h| h.each{ |k,v| (r[k]||=[]) &lt;&lt; v } } }
  end
  def phrogz2b(hashes)
    hashes.each_with_object({}){ |h,r| h.each{ |k,v| (r[k]||=[]) &lt;&lt; v } }
  end
  def phrogz3(hashes)
    result = hashes.inject({}){ |a,b| a.merge(b){ |_,x,y| [*x,*y] } }
    result.each{ |k,v| result[k] = [v] unless v.is_a? Array }
  end
  def glenn1(hs)
    hs.reduce({}) {|h,pairs| pairs.each {|k,v| (h[k] ||= []) &lt;&lt; v}; h}
  end
  def glenn2(hs)
    hs.map(&amp;:to_a).flatten(1).reduce({}) {|h,(k,v)| (h[k] ||= []) &lt;&lt; v; h}
  end
  def fl00r(hs)
    h = Hash.new{|h,k| h[k]=[]}
    hs.map(&amp;:to_a).flatten(1).each{|v| h[v[0]] &lt;&lt; v[1]}
    h
  end
  def sawa(a)
    a.map(&amp;:to_a).flatten(1).group_by{|k,v| k}.each_value{|v| v.map!{|k,v| v}}
  end
  def michael1(hashes)
    h = Hash.new{|h,k| h[k]=[]}
    hashes.each_with_object(h) do |h, result|
      h.each{ |k, v| result[k] &lt;&lt; v }
    end
  end
  def michael2(hashes)
    h = Hash.new{|h,k| h[k]=[]}
    hashes.inject(h) do |result, h|
      h.each{ |k, v| result[k] &lt;&lt; v }
      result
    end
  end
  def tokland1(hs)
    hs.map(&amp;:to_a).flatten(1).map_by{ |k, v| [k, v] }
  end
  def tokland2(hs)
    Hash[hs.map(&amp;:to_a).flatten(1).group_by(&amp;:first).map{ |k, vs|
      [k, vs.map{|o|o[1]}]
    }]
  end
  
  require 'benchmark'
  N = 10_000
  Benchmark.bm do |x|
    x.report('Phrogz 2a'){ TESTS.each{ |n,h| N.times{ phrogz2a(h) } } }
    x.report('Phrogz 2b'){ TESTS.each{ |n,h| N.times{ phrogz2b(h) } } }
    x.report('Glenn 1  '){ TESTS.each{ |n,h| N.times{ glenn1(h)   } } }
    x.report('Phrogz 1 '){ TESTS.each{ |n,h| N.times{ phrogz1(h)  } } }
    x.report('Michael 1'){ TESTS.each{ |n,h| N.times{ michael1(h) } } }
    x.report('Michael 2'){ TESTS.each{ |n,h| N.times{ michael2(h) } } }
    x.report('Glenn 2  '){ TESTS.each{ |n,h| N.times{ glenn2(h)   } } }
    x.report('fl00r    '){ TESTS.each{ |n,h| N.times{ fl00r(h)    } } }
    x.report('sawa     '){ TESTS.each{ |n,h| N.times{ sawa(h)     } } }
    x.report('Tokland 1'){ TESTS.each{ |n,h| N.times{ tokland1(h) } } }
    x.report('Tokland 2'){ TESTS.each{ |n,h| N.times{ tokland2(h) } } }
    x.report('Phrogz 3 '){ TESTS.each{ |n,h| N.times{ phrogz3(h)  } } }
  
  end
  </code></pre>