在 Ruby 中如何在不覆盖重复键的情况下合并两个散列?

有没有一种简单的或 优雅的方法来合并两个哈希而不覆盖重复的关键字?

也就是说,如果键出现在原始散列中,我不想改变它的值。

120733 次浏览

如果你有两个散列,optionsdefaults,你想合并 defaultsoptions而不覆盖现有的关键字,你真正想做的是相反的: 合并 optionsdefaults:

options = defaults.merge(options)

或者,如果你正在使用 Rails,你可以这样做:

options.reverse_merge!(defaults)

如果你的问题是,原始散列和第二个散列都可能有重复的键,你不想覆盖任何一个方向,你可能不得不去一个简单的手动合并,某种冲突检查和处理:

hash2.each_key do |key|
if ( hash1.has_key?(key) )
hash1[ "hash2-originated-#{key}" ] = hash2[key]
else
hash1[key]=hash2[key]
end
end

显然,这是非常基础的,并且假设 hash1没有任何名为“ hash2-Origin-whatever”的键——你最好在键上加上一个数字,这样它就变成了 key1、 key2等等,直到你找到一个还没有在 hash1中的键。另外,我已经几个月没有使用过 Ruby 了,所以语法上可能不太正确,但是你应该能够理解它的要点。

或者将键的值重新定义为数组,这样 hash1[ key ]返回 hash1的原始值和 hash2的值。取决于你真正想要的结果是什么。

在标准 Ruby 库中有一种方法可以合并散列,而不会覆盖现有的值或重新分配散列。

important_hash.merge!(defaults) { |key, important, default| important }

这里你可以通过 reverse_merge合并你的2个散列

order = {
id: 33987,
platform: 'web'
}


user = {
name: 'Jhon Doe',
email: 'jhon.doe@gmail.com'
}
newHash = order.reverse_merge!(user)
render json: { data: newHash, status: 200 }


# => {:name=>"Jhon Doe", :email=>"jhon.doe@gmail.com", :id=>33987, :platform=>"web"}

如果希望在不覆盖目标散列的情况下合并两个散列 optionsdefaults,可以使用 select检查目标散列中是否已经存在密钥。这是一个没有 Rails 的纯 Ruby 解决方案:

options  = { "a" => 100, "b" => 200 }
defaults = { "b" => 254, "c" => 300 }
options.merge!(defaults.select{ |k,_| not options.has_key? k })


# output
# => {"a"=>100, "b"=>200, "c"=>300}

或者,如果密钥存在,但是包含 nil,并且您希望覆盖它:

options.merge!(defaults.select{ |k,_| options[k].nil? })