合并具有相同键的Ruby嵌套哈希

3

我在Ruby中有几个哈希,其中包含嵌套的哈希,并且具有非常相似的结构。它们看起来像这样:

a = {
    "year_1": {
        "sub_type_a": {
            "label1": value1
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2
        }
    }
}

b = {
    "year_1": {
        "sub_type_a": {
            "label3": value3
        }
    },
    "year_2": {
        "sub_type_a": {
            "label4": value4
        }
    }
}

c = {
    "year_1": {
        "sub_type_a": {
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label6": value6
        }
    }
}

我希望将它们合并到一个哈希(Hash)中,尽可能地将嵌套数据合并,而不会覆盖其他值,如下所示:
result = {
    "year_1": {
        "sub_type_a": {
            "label1": value1,
            "label3": value3,
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2,
            "label4": value4,
            "label6": value6
        }
    }
}

可能会有不止一个子类型,而不仅仅是一个,但这是基本的想法。

如果我使用merge函数,它只会覆盖子类型散列中的标签值数据,然后我就只剩下一条记录了。

有没有简单的方法可以实现这一点?我可以编写一个递归迭代散列的函数,在其中找出应添加在哪里的内容,但感觉应该有更简单的方法。

4个回答

4

类似的东西。

结合each_with_objecteachmerge,使得你可以遍历每个哈希表,并在新建的临时哈希表中分配合并的值(当它们存在时):

[a, b, c].each_with_object({}) do |years_data, hash|
  years_data.each do |year, data|
    hash[year] = (hash[year] || {}).merge(data) { |_, oldval, newval| oldval.merge(newval) }
  end
end
# {
#     :year_1 => {
#         :sub_type_a => {
#             :label1 => :value1,
#             :label3 => :value3,
#             :label5 => :value5
#         }
#     },
#     :year_2 => {
#         :sub_type_a => {
#             :label2 => :value2,
#             :label4 => :value4,
#             :label6 => :value6
#         }
#     }
# }

1
如果你正在使用Rails(或ActiveSupport),你可能想看看deep_merge,它可以为你处理嵌套哈希的合并。

2
这应该是一个注释。 - Cary Swoveland

1
我们被给予以下内容。
a = {:year_1=>{:sub_type_a=>{:label1=>"value1"}},
     :year_2=>{:sub_type_a=>{:label2=>"value2"}}} 

b = {:year_1=>{:sub_type_a=>{:label3=>"value3"}},
     :year_2=>{:sub_type_a=>{:label4=>"value4"}}} 

c = {:year_1=>{:sub_type_a=>{:label5=>"value5"}},
     :year_2=>{:sub_type_a=>{:label6=>"value6"}}}

arr = [a, b, c]

我们可以按以下方式构造所需的哈希。
arr.each_with_object({}) do |g,h|
  g.each do |yr,v|
    k,f = v.first
    h.update(yr=>{ k=>f }) { |_,o,n| { k=>o[k].merge(n[k]) } }
  end
end
  #=> {:year_1=>{:sub_type_a=>{:label1=>"value1", :label3=>"value3",
  #                            :label5=>"value5"}},
  #    :year_2=>{:sub_type_a=>{:label2=>"value2", :label4=>"value4",
  #                            :label6=>"value6"}}}  

这里使用了Hash#update形式(也称为merge!),它采用块来确定合并的两个哈希中存在的键的值。请参见链接以了解该块的三个块变量的说明。我已经使用下划线(一个有效的局部变量)作为第一个块变量,即公共键,以向读者表明它在块计算中未被使用。这是一个常见的约定。
对于任何对计算细节感兴趣的人(最可靠的理解方法),我将添加一些puts语句来执行代码。
arr.each_with_object({}) do |g,h|
  puts "g=#{g}"
  puts "h=#{h}"
  g.each do |yr,v|
    puts "  yr=#{yr}"
    puts "  v=#{v}"
    k,f = v.first
    puts "  k=#{k}"
    puts "  f=#{f}"
    puts "  yr=>{ k=>f } = #{yr}=>#{v} = #{{ yr=>v }}"
    h.update(yr=>{ k=>f }) do |_,o,n|
      puts "    _=#{_}"
      puts "    o=#{o}"
      puts "    n=#{n}"
      puts "    { k=>o[k].merge(n[k]) }"
      puts "      => { #{k}=>#{o[k]}.merge(#{n[k]}) }"           
      { k=>o[k].merge(n[k]) }.tap { |e| puts "      => #{e}" }
    end
  end
end

以下内容显示为:

以下是显示的内容。

g={:year_1=>{:sub_type_a=>{:label1=>"value1"}},
   :year_2=>{:sub_type_a=>{:label2=>"value2"}}}
h={}
  yr=year_1
  v={:sub_type_a=>{:label1=>"value1"}}
  k=sub_type_a
  f={:label1=>"value1"}
  yr=>{ k=>f } = year_1=>{:sub_type_a=>{:label1=>"value1"}} = 
    {:year_1=>{:sub_type_a=>{:label1=>"value1"}}}
  yr=year_2
  v={:sub_type_a=>{:label2=>"value2"}}
  k=sub_type_a
  f={:label2=>"value2"}
  yr=>{ k=>f } = year_2=>{:sub_type_a=>{:label2=>"value2"}} =
    {:year_2=>{:sub_type_a=>{:label2=>"value2"}}}

g={:year_1=>{:sub_type_a=>{:label3=>"value3"}},
   :year_2=>{:sub_type_a=>{:label4=>"value4"}}}
h={:year_1=>{:sub_type_a=>{:label1=>"value1"}},
   :year_2=>{:sub_type_a=>{:label2=>"value2"}}}
  yr=year_1
  v={:sub_type_a=>{:label3=>"value3"}}
  k=sub_type_a
  f={:label3=>"value3"}
  yr=>{ k=>f } = year_1=>{:sub_type_a=>{:label3=>"value3"}} =
    {:year_1=>{:sub_type_a=>{:label3=>"value3"}}}
    _=year_1
    o={:sub_type_a=>{:label1=>"value1"}}
    n={:sub_type_a=>{:label3=>"value3"}}
    { k=>o[k].merge(n[k]) }
      => { sub_type_a=>{:label1=>"value1"}.
           merge({:label3=>"value3"}) }
      => {:sub_type_a=>{:label1=>"value1", :label3=>"value3"}}
yr=year_2
  v={:sub_type_a=>{:label4=>"value4"}}
  k=sub_type_a
  f={:label4=>"value4"}
  yr=>{ k=>f } = year_2=>{:sub_type_a=>{:label4=>"value4"}} =
    {:year_2=>{:sub_type_a=>{:label4=>"value4"}}}
    _=year_2
    o={:sub_type_a=>{:label2=>"value2"}}
    n={:sub_type_a=>{:label4=>"value4"}}
    { k=>o[k].merge(n[k]) }
      => { sub_type_a=>{:label2=>"value2"}.
           merge({:label4=>"value4"}) }
      => {:sub_type_a=>{:label2=>"value2", :label4=>"value4"}}

g={:year_1=>{:sub_type_a=>{:label5=>"value5"}},
   :year_2=>{:sub_type_a=>{:label6=>"value6"}}}
h={:year_1=>{:sub_type_a=>{:label1=>"value1", :label3=>"value3"}},
   :year_2=>{:sub_type_a=>{:label2=>"value2", :label4=>"value4"}}}
  yr=year_1
  v={:sub_type_a=>{:label5=>"value5"}}
  k=sub_type_a
  f={:label5=>"value5"}
  yr=>{ k=>f } = year_1=>{:sub_type_a=>{:label5=>"value5"}} =
    {:year_1=>{:sub_type_a=>{:label5=>"value5"}}}
    _=year_1
    o={:sub_type_a=>{:label1=>"value1", :label3=>"value3"}}
    n={:sub_type_a=>{:label5=>"value5"}}
    { k=>o[k].merge(n[k]) }
      => { sub_type_a=>{:label1=>"value1", :label3=>"value3"}.
           merge({:label5=>"value5"}) }
      => {:sub_type_a=>{:label1=>"value1", :label3=>"value3",
          :label5=>"value5"}}
  yr=year_2
  v={:sub_type_a=>{:label6=>"value6"}}
  k=sub_type_a
  f={:label6=>"value6"}
  yr=>{ k=>f } = year_2=>{:sub_type_a=>{:label6=>"value6"}} =
    {:year_2=>{:sub_type_a=>{:label6=>"value6"}}}
    _=year_2
    o={:sub_type_a=>{:label2=>"value2", :label4=>"value4"}}
    n={:sub_type_a=>{:label6=>"value6"}}
    { k=>o[k].merge(n[k]) }
      => { sub_type_a=>{:label2=>"value2", :label4=>"value4"}.
           merge({:label6=>"value6"}) }
      => {:sub_type_a=>{:label2=>"value2", :label4=>"value4",
                        :label6=>"value6"}}
 => {:year_1=>{:sub_type_a=>{:label1=>"value1", :label3=>"value3",
       :label5=>"value5"}},
     :year_2=>{:sub_type_a=>{:label2=>"value2", :label4=>"value4",
       :label6=>"value6"}}} 

-1

Hash#merge 接受一个可选的 冲突解决块,每当主题和参数中都存在一个键时,该块将被调用。

您可以使用此功能来递归合并哈希。


1
这应该是一条注释。 - Cary Swoveland
2
这没有意义。这是对问题的回答,而不是对它的评论。 - Masklinn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接