I've searched through all the answers regarding the use of deep_merge; however, I am still having trouble with my particular issue. I'm trying to merge 2 hashes and add a specific key for each match. For example:
UPDATED THE FORMAT
Hash 1:
{
"actions"=> [
{
"causes"=> [
{
"shortDescription"=>"short description for run 1",
"userId"=>"user.a"
}
]
}
],
"artifacts"=> [],
"fullDisplayName"=>"Run #1",
"result"=>"FAILURE",
"changeSet"=> {
"items"=>[],
"kind"=>nil
},
"culprits"=> []
}
Hash 2:
{
"actions"=> [
{
"causes"=> [
{
"shortDescription"=>"short description for run 2",
"userId"=>"user.b"
}
]
}
],
"artifacts"=> [],
"fullDisplayName"=>"Run #2",
"result"=>"FAILURE",
"changeSet"=> {
"items"=>[],
"kind"=>nil
},
"culprits"=> []
}
Key list:
["key-one","key-two"]
I would like the resulting hash to be:
{
"actions"=> [
{
"causes"=> [
{
"shortDescription"=> {
"key-one" => "short description for run 1",
"key-two" => "short description for run 2"
},
"userId"=> {
"key-one" => "user.a",
"key-two" => "user.b"
}
}
]
}
],
"artifacts"=> {
"key-one" => [],
"key-two" => []
},
"fullDisplayName"=> {
"key-one" => "Run #1",
"key-two" => "Run #2"
},
"result"=> {
"key-one" => "FAILURE",
"key-two" => "FAILURE"
},
"changeSet"=> {
"items"=> {
"key-one" => [], "key-two" => []
},
"kind"=> {
"key-one" => nil,
"key-two" => nil
}
},
"culprits"=> {
"key-one" => [],
"key-two" => []
}
}
If h1 and h2 are the two hashes you wish to merge, and h3 is the desired result, I think the following should work:
#merge_key1, #merge_key2 = "key-one", "key-two"
def merge_em(g1, g2)
case g1
when Array
g1.size.times {|i| merge_em(g1[i], g2[i])}
when Hash
g1.keys.each do |k|
v = g1[k]
if (Hash === v || (Array === v && !v.empty?))
merge_em(v, g2[k])
else
g1[k] = {#merge_key1 => v, #merge_key2 => g2[k]}
end
end
end
end
h = Marshal.load(Marshal.dump(h1))
merge_em(h, h2)
p (h == h3) # => true
A few notes:
This solution depends on the hash structures; it may not work if the structures are changed.
Marshal.load(Marshal.dump(h1)) is used to make a "deep copy" of h1, so that h1 is not modified. If h1 can be modified, merge_em(h1, h2) is sufficient, with h1 being the merged hash.
The gem awesome_print gives you a nicely-formatted display of complex structures like these. All you'd need to do here is require 'ap' followed by ap h. Try it!
Related
I have a hash like this:
hash = {
'en-us': {
where: 'USA'
},
'en-us-zone2': {
where: 'USA'
},
'en-nl': {
where: 'Netherlands'
},
'en-pt': {
where: 'Portugal'
},
}
And I tried to group them using group_by:
result = hash.group_by { |k,v| v[:where] }
However, It returns full of array not by array of hashes. So here is expected and actual results:
Actual Result:
{ "USA"=>
[[:"en-us", {:where=>"USA"}], [:"en-us-zone2", {:where=>"USA"}]],
"Netherlands"=>
[[:"en-nl", {:where=>"Netherlands"}]],
"Portugal"=>
[[:"en-pt", {:where=>"Portugal"}]]
}
Expected Result:
{ "USA"=>
[{:"en-us" => {:where=>"USA"}}, {:"en-us-zone2" => {:where=>"USA"}}]
"Netherlands"=>
[{:"en-nl" => {:where=>"Netherlands"}}]
"Portugal"=>
[{:"en-pt" => {:where=>"Portugal"}}]
}
See, Actual is Array of arrays, instead of array of hashes. Hash keys becomes first array element.
How can I group my hash based on :where ?
This should work:
hash.group_by { |k,v| v[:where] }.each { |_, v| v.map! { |array| { array[0] => array[1] } } }
Or with transform_values
hash.group_by { |k,v| v[:where] }.transform_values { |v| v.map { |array| {array[0] => array[1] } } }
https://ruby-doc.org/core-2.4.0/Hash.html#method-i-transform_values
It's ugly but works:
hash = {
'en-us': {
where: 'USA'
},
'en-us-zone2': {
where: 'USA'
},
'en-nl': {
where: 'Netherlands'
},
'en-pt': {
where: 'Portugal'
},
}
hash.
group_by { |_, v| v[:where] }.
map do |k, v|
[
k,
v.map { |a, b| {a => b} }
]
end.to_h
# => {"USA"=>[{:"en-us"=>{:where=>"USA"}}, {:"en-us-zone2"=>{:where=>"USA"}}], "Netherlands"=>[{:"en-nl"=>{:where=>"Netherlands"}}], "Portugal"=>[{:"en-pt"=>{:where=>"Portugal"}}]}
I have a iteration here:
container = []
summary_data.each do |_index, data|
container << data
end
The structure of the summary_data is listed below:
summary_data = {
"1" => { orders: { fees: '25.00' } },
"3" => { orders: { fees: '30.00' } },
"6" => { orders: { fees: '45.00' } }
}
I want to remove the numeric key, e.g., "1", "3".
And I expect to get the following container:
[
{
"orders": {
"fees": "25.00"
}
},
{
"orders": {
"fees": "30.00"
}
},
{
"orders": {
"fees": "45.00"
}
}
]
Is there a more efficient way to refactor the code above?
Appreciate for any help.
You can use Hash#values method, like this:
container = summary_data.values
If the inner hashes all have the same structure, the only interesting information are the fees:
summary_data.values.map{|h| h[:orders][:fees] }
# => ["25.00", "30.00", "45.00"]
If you want to do some calculations with those fees, you could convert them to numbers:
summary_data.values.map{|h| h[:orders][:fees].to_f }
# => [25.0, 30.0, 45.0]
It might be even better to work with cents as integers to avoid any floating point error:
summary_data.values.map{|h| (h[:orders][:fees].to_f * 100).round }
=> [2500, 3000, 4500]
You need an array having values of provided hash. You can get by values method directly.
summary_data.values
Trying to assign part of a has to another variable. I have a hash. Something like:
hash = {
"cupcake" => {
"a" => 1
},
"muffin" => {
"b" => 2
}
}
When I do something like:
cupcake = hash["cupcake"]
cupcake is nil after this code.
If you want string keys you have to use this syntax
hash = {
"cupcake" => {
"a" => 1
},
"muffin" => {
"b" => 2
}
}
Syntax with colons is for symbol keys
hash = {
cupcake: {
a: 1
},
muffin: {
b: 2
}
}
cupcake = hash[:cupcake]
I have this array:
params[:types] = [type1, type2, type3...]
I would like to populate my hash the following way using the above array:
params[:hash] = {
"type1" => {
something: something
},
"type2" => {
something: something
},
}
Using a for loop like for index in i ...params[:types] just populates the hash with the last value in the array.
You can use the each_with_object method to do this:
params = {}
params[:types] = ["type1", "type2", "type3"]
params[:types].each_with_object({}) { |k, h| h[k] = { "something" => "something" } }
That last line will return:
=> {"type1"=>{"something"=>"something"}, "type2"=>{"something"=>"something"}, "type3"=>{"something"=>"something"}}
Here is a code snippet example that does what you need.
hash = {}
array.each do |a|
hash[a.to_s] = { "something" => "something" }
end
output:
hash
=> {
"type1" => {
"something" => "something"
},
"type2" => {
"something" => "something"
},
"type3" => {
"something" => "something"
}
}
You could do this:
params = { types: ["type1", "type2", "type3"] }
Hash[params[:types].product([{"something" => "something"}])]
#=> {"type1"=>{"something"=>"something"},
# "type2"=>{"something"=>"something"},
# "type3"=>{"something"=>"something"}}
or with Ruby 2.1,
params[:types].product([{"something" => "something"}]).to_h
If you want a different hash for each element of params[:types]:
hashes = [{ "something1"=>"something1" }, { "something2"=>"something2" },
{ "something3"=>"something3" }]
then
Hash[params[:types].zip(hashes)]
#=> {"type1"=>{"something1"=>"something1"},
# "type2"=>{"something2"=>"something2"},
# "type3"=>{"something3"=>"something3"}}
How can split this array of hashes in two based on the value of the ate key?
array = [
{ name: "Gad", ate: true },
{ name: "Lad", ate: false },
{ name: "Bad", ate: true },
{ name: "Sad", ate: false }
]
Example output
array_1 = [
{ name: "Gad", ate: true },
{ name: "Bad", ate: true }
]
array_2 = [
{ name: "Lad", ate: false },
{ name: "Sad", ate: false }
]
Use the Enumerable#partition method:
array.partition { |x| x[:ate] }
# => [[{:name=>"Gad", :ate=>true}, {:name=>"Bad", :ate=>true}],
# [{:name=>"Lad", :ate=>false}, {:name=>"Sad", :ate=>false}]]
Or:
array_1, array_2 = array.partition { |x| x[:ate] }
array_1
# => [{:name=>"Gad", :ate=>true}, {:name=>"Bad", :ate=>true}]
array_2
# => [{:name=>"Lad", :ate=>false}, {:name=>"Sad", :ate=>false}]
array_one, array_two = *array.group_by { |x| x[:ate] }.map(&:last)
=> array_one
=> # [{:name=>"Gad", :ate=>true}, {:name=>"Bad", :ate=>true}]
=> array_two
=> # [{:name=>"Lad", :ate=>false}, {:name=>"Sad", :ate=>false}]
thx #CarySwoveland
I can't compete with partition, but here's another way:
trues = array.select { |h| h[:ate] }
falses = array - trues