使用相同的键合并Ruby嵌套哈希

使用相同的键合并Ruby嵌套哈希,ruby,hash-of-hashes,Ruby,Hash Of Hashes,我在Ruby中有几个散列,它们内部有嵌套的散列,结构非常相似。它们看起来像这样: a = { "year_1": { "sub_type_a": { "label1": value1 } }, "year_2": { "sub_type_a": { "label2": value2 } } } b = { "year_1": {

我在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
        }
    }
}
result = {
    "year_1": {
        "sub_type_a": {
            "label1": value1,
            "label3": value3,
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2,
            "label4": value4,
            "label6": value6
        }
    }
}
我想将它们合并到一个散列中,这样就可以在不覆盖其他值的情况下尽可能地合并嵌套数据:

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
        }
    }
}
result = {
    "year_1": {
        "sub_type_a": {
            "label1": value1,
            "label3": value3,
            "label5": value5
        }
    },
    "year_2": {
        "sub_type_a": {
            "label2": value2,
            "label4": value4,
            "label6": value6
        }
    }
}
也可以有几个子类型,而不是只有一个,但这是一般的想法

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

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

Hash#merge
采用可选的冲突解决块,只要主题和参数中都有密钥,就会调用该块


您可以使用它来递归地合并哈希。

我们得到以下信息

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"}}}  
它使用(也称为
merge!
)的形式,使用块来确定合并的两个哈希中存在的键的值。有关该块的三个块变量的说明,请参见链接。我使用下划线(一个有效的局部变量)作为第一个块变量,即公共键,以向读者发出在块计算中不使用它的信号。这是一个普遍的惯例

对于任何对计算的血淋淋的细节感兴趣的人(理解发生了什么的一种可靠方法),我将执行添加了一些
put
语句的代码

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"}}}

类似的东西

每个\u与\u对象
每个
合并
组合,这样您就可以通过每个散列进行迭代,并在合并值存在时将其分配给临时新值:

[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
#         }
#     }
# }

如果您使用的是Rails(或ActiveSupport),您可能想看看它,它为您处理嵌套哈希的合并

这应该是一条注释。这应该是一条注释。这没有任何意义。这是对问题的回答,而不是评论。