38
h  = { a: 1 }
h2 = { b: 2 }
h3 = { c: 3 }

Hash#merge 适用于 2 个哈希:h.merge(h2)

如何合并 3 个哈希?

h.merge(h2).merge(h3)有效,但有更好的方法吗?

4

10 回答 10

54

你可以这样做:

h, h2, h3  = { a: 1 }, { b: 2 }, { c: 3 }
a  = [h, h2, h3]

p Hash[*a.map(&:to_a).flatten] #= > {:a=>1, :b=>2, :c=>3}

编辑:如果你有很多哈希,这可能是正确的方法:

a.inject{|tot, new| tot.merge(new)}
# or just
a.inject(&:merge)
于 2013-10-23T17:40:02.843 回答
21

由于Ruby 2.0可以更优雅地完成:

h.merge **h1, **h2

在重叠键的情况下 - 当然,后者优先:

h  = {}
h1 = { a: 1, b: 2 }
h2 = { a: 0, c: 3 }

h.merge **h1, **h2
# => {:a=>0, :b=>2, :c=>3}

h.merge **h2, **h1
# => {:a=>1, :c=>3, :b=>2}
于 2017-06-28T11:22:38.030 回答
19

你可以做

[*h,*h2,*h3].to_h
# => {:a=>1, :b=>2, :c=>3}

无论键是否为Symbols,这都有效。

于 2017-10-05T18:34:39.190 回答
15

Ruby 2.6 允许merge采用多个参数:

h  = { a: 1 }
h2 = { b: 2 }
h3 = { c: 3 }
h4 = { 'c' => 4 }
h5 = {}

h.merge(h2, h3, h4, h5) # => {:a=>1, :b=>2, :c=>3, "c"=>4}

这也适用于Hash.merge!Hash.update。文档在这里

还将空哈希和键作为符号字符串。

简单得多:)

于 2018-11-16T17:18:24.950 回答
7

reduce使用(same as inject)回答

hash_arr = [{foo: "bar"}, {foo2: "bar2"}, {foo2: "bar2b", foo3: "bar3"}]

hash_arr.reduce { |acc, h| (acc || {}).merge h }
# => {:foo2=>"bar2", :foo3=>"bar3", :foo=>"bar"}

解释

对于那些开始使用 Ruby 或函数式编程的人,我希望这个简短的解释可能有助于理解这里发生的事情。

reduce当在 Array 对象 () 上调用该方法时,hash_arr将遍历数组的每个元素,并将块的返回值存储在累加器 ( acc) 中。实际上,h我的块的参数将采用数组中每个哈希的值,并且acc参数将采用块通过每次迭代返回的值。

我们(acc || {})用来处理accnil 的初始条件。请注意,该merge方法优先考虑原始哈希中的键/值。这就是为什么"bar2b"我的最终哈希中没有出现的值。

希望有帮助!

于 2017-08-26T13:49:56.833 回答
6

要以@Oleg Afanasyev 的回答为基础,您还可以使用这个巧妙的技巧:

h  = { a: 1 }
h2 = { b: 2 }
h3 = { c: 3 }

z = { **h, **h2, **h3 }  # => {:a=>1, :b=>2, :c=>3}

干杯!

于 2017-10-05T20:37:30.900 回答
4
class Hash  
  def multi_merge(*args)
    args.unshift(self)
    args.inject { |accum, ele| accum.merge(ele) }
  end
end

那应该这样做。正如我所展示的,您可以轻松地将其修补到 Hash 中。

于 2013-10-23T19:08:53.890 回答
3
newHash = [h, h2, h3].each_with_object({}) { |oh, nh| nh.merge!(oh)}
# => {:a=>1, :b=>2, :c=>3}
于 2013-10-23T19:59:15.703 回答
3

这是我们在应用程序中使用的 2 个 monkeypatched ::Hash 实例方法。由 Minitest 规范支持。出于性能原因,它们使用merge!而不是merge内部使用。

class ::Hash

  # Merges multiple Hashes together. Similar to JS Object.assign.
  #   Returns merged hash without modifying the receiver.
  #
  # @param *other_hashes [Hash]
  #
  # @return [Hash]
  def merge_multiple(*other_hashes)
    other_hashes.each_with_object(self.dup) do |other_hash, new_hash|
      new_hash.merge!(other_hash)
    end
  end

  # Merges multiple Hashes together. Similar to JS Object.assign.
  #   Modifies the receiving hash.
  #   Returns self.
  #
  # @param *other_hashes [Hash]
  #
  # @return [Hash]
  def merge_multiple!(*other_hashes)
    other_hashes.each(&method(:merge!))

    self
  end

end

测试:

describe "#merge_multiple and #merge_multiple!" do
  let(:hash1) {{
    :a => "a",
    :b => "b"
  }}
  let(:hash2) {{
    :b => "y",
    :c => "c"
  }}
  let(:hash3) {{
    :d => "d"
  }}
  let(:merged) {{
    :a => "a",
    :b => "y",
    :c => "c",
    :d => "d"
  }}

  describe "#merge_multiple" do
    subject { hash1.merge_multiple(hash2, hash3) }

    it "should merge three hashes properly" do
      assert_equal(merged, subject)
    end

    it "shouldn't modify the receiver" do
      refute_changes(->{ hash1 }) do
        subject
      end
    end
  end

  describe "#merge_multiple!" do
    subject { hash1.merge_multiple!(hash2, hash3) }

    it "should merge three hashes properly" do
      assert_equal(merged, subject)
    end

    it "shouldn't modify the receiver" do
      assert_changes(->{ hash1 }, :to => merged) do
        subject
      end
    end
  end
end
于 2017-02-17T20:07:04.887 回答
2

只是为了好玩,你也可以这样做:

a = { a: 1 }, { b: 2 }, { c: 3 }
{}.tap { |h| a.each &h.method( :update ) }
#=> {:a=>1, :b=>2, :c=>3}
于 2013-10-24T06:35:49.717 回答