76

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}
potashin
  • 44,205
  • 11
  • 83
  • 107
evfwcqcg
  • 15,755
  • 15
  • 55
  • 72

7 Answers7

131

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}
Joshua Cheek
  • 30,436
  • 16
  • 74
  • 83
Howard
  • 38,639
  • 9
  • 64
  • 83
  • 42
    `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)` – tokland Jun 08 '12 at 07:47
  • @tokland, post your comment as a separate answer - it should get more visibility – Jason Sep 29 '14 at 12:47
  • If your application allows for it, the `:update` version suggested by tokland is the faster option. – Greg Tarsa May 16 '19 at 17:29
58

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

potashin
  • 44,205
  • 11
  • 83
  • 107
25

These two are equivalent (reduce/inject are the same method):

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)

Alternatively, you can convert the hashes to pairs and then build the final hash:

total_hash = hs.flat_map(&:to_a).to_h
tokland
  • 66,169
  • 13
  • 144
  • 170
5

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)
bigsolom
  • 2,309
  • 1
  • 19
  • 15
  • 2
    This result confused me. The [docs](https://docs.ruby-lang.org/en/2.6.0/Enumerable.html#method-i-inject) 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 May 16 '19 at 17:23
2

Just use

a.reduce(:merge)
#=> {:a=>:b, :c=>:d}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Ritesh katare
  • 132
  • 3
  • 11
0

Try this

a.inject({}){|acc, hash| acc.merge(hash)} #=> {:a=>:b, :c=>:d}
Rahul Patel
  • 1,386
  • 1
  • 14
  • 16
0

You can transform it to array [[:a, :b]] and after that translate everything to hash {:a=>:b}

# it works like [[:a, :b]].to_h => {:a=>:b}

[{a: :b}, {c: :d}].map { |hash| hash.to_a.flatten }.to_h

# => {:a=>:b, :c=>:d}
Vlad Hilko
  • 1,104
  • 12
  • 17