Reputation: 207
i'm working on a version control system in which chef cookbook version numbers are specified in multiple different environments.
I've been able to merge the two environment files together using this format --
source = JSON.parse(File.read("A.json"))
destination = JSON.parse(File.read("B.json"))
source = destination.merge(source)
The values of each file are in this format --
'A'
{
"a": "v2.0.18",
"b": "v5.0.2",
"c": "v17.0.0",
"d": "v9.0.0",
}
'B'
{
"a": "v1.0.18",
"b": "v4.0.0",
"c": "v20.0.0",
"d": "v7.0.0"
}
Currently does --
{
"a": "v2.0.18",
"b": "v5.0.2",
"c": "v17.0.0",
"d": "v9.0.0",
}
What i'd like it to do --
{
"a": "v2.0.18",
"b": "v5.0.2",
"c": "v20.0.0", #keeps higher value
"d": "v9.0.0",
}
Any help would be greatly appreciated. Thanks
Upvotes: 0
Views: 131
Reputation: 52357
Hash#merge
is what you're looking for:
a.merge(b) do |key, old_val, new_val|
Gem::Version.new(old_val[1..-1]) > Gem::Version.new(new_val[1..-1]) ? old_val : new_val
end
#=> {:a=>"v2.0.18", :b=>"v5.0.2", :c=>"v20.0.0", :d=>"v9.0.0"}
As @Stefan suggested, the above could be improved by incorporating the approach @Eric Duminil used in his answer:
a.merge(b) { |key, *values| values.max_by { |v| Gem::Version.new(v[1..-1]) } }
Upvotes: 5
Reputation: 54223
You can use Hash#merge
, but you also need to define how the comparison is made between two strings.
major_minor
converts "v2.0.18"
to [2,0,18]
, which can be compared to other version arrays to find the maximum.
source = {
"a": "v2.0.18",
"b": "v5.0.2",
"c": "v17.0.0",
"d": "v9.0.0",
}
destination = {
"a": "v1.0.18",
"b": "v4.0.0",
"c": "v20.0.0",
"d": "v7.0.0"
}
def major_minor(version)
version.scan(/\d+/).map(&:to_i)
end
p source.merge(destination){|key, old, new| [old, new].max_by{|v| major_minor(v) } }
#=> {:a=>"v2.0.18", :b=>"v5.0.2", :c=>"v20.0.0", :d=>"v9.0.0"}
Upvotes: 5
Reputation: 2351
merge
takes additional argument block to figure out which key to keep in case both hashes has same key
a = {
"a": "v2.0.18",
"b": "v5.0.2",
"c": "v17.0.0",
"d": "v9.0.0",
}
b = {
"a": "v1.0.18",
"b": "v4.0.0",
"c": "v20.0.0",
"d": "v7.0.0"
}
c = a.merge(b) {|k, v1, v2| [v1, v2].max}
=> {:a=>"v2.0.18", :b=>"v5.0.2", :c=>"v20.0.0", :d=>"v9.0.0"}
Upvotes: -1
Reputation: 2498
You can use merge passing a block to choose which value should be chosen when the key is duplicated.
Upvotes: 2