Array of hashes to hash

33,016

Solution 1

You may use

a.reduce Hash.new, :merge

which directly yields

{:a=>:b, :c=>:d}

Note that in case of collisions the order is important. Latter hashes override previous mappings, see e.g.:

[{a: :b}, {c: :d}, {e: :f, a: :g}].reduce Hash.new, :merge   # {:a=>:g, :c=>:d, :e=>:f}

Solution 2

You can use .inject:

a.inject(:merge)
#=> {:a=>:b, :c=>:d}

Demonstration

Which initiates a new hash on each iteration from the two merged. To avoid this, you can use destructive :merge!( or :update, which is the same):

a.inject(:merge!)
#=> {:a=>:b, :c=>:d}

Demonstration

Solution 3

These two are equivalent:

total_hash = hs.reduce({}) { |acc_hash, hash| acc_hash.merge(hash) }
total_hash = hs.reduce({}, :merge)

Note that Hash#merge creates a new hash on each iteration, which may be a problem if you are building a big one. In that case, use update instead:

total_hash = hs.reduce({}, :update)

You can also convert the hashes to pairs and then build the final hash:

total_hash = hs.flat_map(&:to_a).to_h

Solution 4

I came across this answer and I wanted to compare the two options in terms of performance to see which one is better:

  1. a.reduce Hash.new, :merge
  2. a.inject(:merge)

using the ruby benchmark module, it turns out that option (2) a.inject(:merge) is faster.

code used for comparison:

require 'benchmark'

input = [{b: "c"}, {e: "f"}, {h: "i"}, {k: "l"}]
n = 50_000

Benchmark.bm do |benchmark|
  benchmark.report("reduce") do
    n.times do
      input.reduce Hash.new, :merge
    end
  end

  benchmark.report("inject") do
    n.times do
      input.inject(:merge)
    end
  end
end

the results were

       user     system      total        real
reduce  0.125098   0.003690   0.128788 (  0.129617)
inject  0.078262   0.001439   0.079701 (  0.080383)

Solution 5

Just use

a.reduce(:merge)
#=> {:a=>:b, :c=>:d}
Share:
33,016
evfwcqcg
Author by

evfwcqcg

Updated on July 05, 2022

Comments

  • evfwcqcg
    evfwcqcg almost 2 years

    For example, I have array of single hashes

    a = [{a: :b}, {c: :d}]
    

    What is best way to convert it into this?

    {a: :b, c: :d}
    
  • tokland
    tokland about 12 years
    Hash.new, or as friends like to call him, {} :-) So much as I like pure functional solution, note that merge will create a new hash on every iteration; we can use update instead (it won't mess up with the input hashes, that's the important point): hs.reduce({}, :update)
  • Jason
    Jason over 9 years
    @tokland, post your comment as a separate answer - it should get more visibility
  • Paul Danelli
    Paul Danelli over 5 years
    Thats crazy elegant. Thank you.
  • Greg Tarsa
    Greg Tarsa about 5 years
    This result confused me. The docs say reduce and inject are aliased. A quick check w/ your test shows the slowdown is due to Hash.new as the initializer. :merge creates a new hash each iteration. :update doesn't. So, a re-run with :update shows, even with the Hash.new, the :update version is faster:``` user system total real reduce w/ Hash.new & :update 0.056754 0.002097 0.058851 ( 0.059330) reduce w/ :merge only 0.090021 0.001081 0.091102 ( 0.091257)```
  • Greg Tarsa
    Greg Tarsa about 5 years
    If your application allows for it, the :update version suggested by tokland is the faster option.