这里是更好的递归合并解决方案,它使用refinements并具有bang方法以及块支持 .此代码确实适用于 纯 Ruby。
module HashRecursive
refine Hash do
def merge(other_hash, recursive=false, &block)
if recursive
block_actual = Proc.new {|key, oldval, newval|
newval = block.call(key, oldval, newval) if block_given?
[oldval, newval].all? {|v| v.is_a?(Hash)} ? oldval.merge(newval, &block_actual) : newval
}
self.merge(other_hash, &block_actual)
else
super(other_hash, &block)
end
end
def merge!(other_hash, recursive=false, &block)
if recursive
self.replace(self.merge(other_hash, recursive, &block))
else
super(other_hash, &block)
end
end
end
end
using HashRecursive
using HashRecursive 执行后,您可以使用默认的Hash::merge 和Hash::merge!,就好像它们没有被修改一样。您可以像以前一样在这些方法中使用blocks。
新功能是您可以将布尔值recursive(第二个参数)传递给这些修改后的方法,它们将递归地合并哈希。
示例写在this answer。这是一个高级示例。
这个问题中的示例很糟糕,因为它与递归合并无关。以下行将满足问题的示例:
a.merge!(b) {|k,v1,v2| [v1, v2].all? {|v| v.is_a?(Array)} ? v1+v2 : v2}
让我给你一个更好的例子来展示上面代码的威力。想象一下两个房间,每个房间里都有一个书架。每个书架有 3 排,每个书架目前有 2 本书。代码:
room1 = {
:shelf => {
:row1 => [
{
:title => "Hamlet",
:author => "William Shakespeare"
}
],
:row2 => [
{
:title => "Pride and Prejudice",
:author => "Jane Austen"
}
]
}
}
room2 = {
:shelf => {
:row2 => [
{
:title => "The Great Gatsby",
:author => "F. Scott Fitzgerald"
}
],
:row3 => [
{
:title => "Catastrophe Theory",
:author => "V. I. Arnol'd"
}
]
}
}
我们要将书从第二个房间的书架上移到第一个房间书架上的同一行。首先,我们将在不设置recursive 标志的情况下执行此操作,即与使用未修改的Hash::merge! 相同:
room1.merge!(room2) {|k,v1,v2| [v1, v2].all? {|v| v.is_a?(Array)} ? v1+v2 : v2}
puts room1
输出会告诉我们第一个房间的架子看起来像这样:
room1 = {
:shelf => {
:row2 => [
{
:title => "The Great Gatsby",
:author => "F. Scott Fitzgerald"
}
],
:row3 => [
{
:title => "Catastrophe Theory",
:author => "V. I. Arnol'd"
}
]
}
}
如您所见,没有recursive 迫使我们扔掉我们珍贵的书籍。
现在我们将做同样的事情,但将recursive 标志设置为true。您可以将 recursive=true 或 true 作为第二个参数传递:
room1.merge!(room2, true) {|k,v1,v2| [v1, v2].all? {|v| v.is_a?(Array)} ? v1+v2 : v2}
puts room1
现在输出会告诉我们,我们实际上移动了我们的书:
room1 = {
:shelf => {
:row1 => [
{
:title => "Hamlet",
:author => "William Shakespeare"
}
],
:row2 => [
{
:title => "Pride and Prejudice",
:author => "Jane Austen"
},
{
:title => "The Great Gatsby",
:author => "F. Scott Fitzgerald"
}
],
:row3 => [
{
:title => "Catastrophe Theory",
:author => "V. I. Arnol'd"
}
]
}
}
最后一次执行可以改写如下:
room1 = room1.merge(room2, recursive=true) do |k, v1, v2|
if v1.is_a?(Array) && v2.is_a?(Array)
v1+v2
else
v2
end
end
puts room1
或
block = Proc.new {|k,v1,v2| [v1, v2].all? {|v| v.is_a?(Array)} ? v1+v2 : v2}
room1.merge!(room2, recursive=true, &block)
puts room1
就是这样。也看看我的递归版本Hash::each(Hash::each_pair)here。