Merging multi-dimensional hashes in Ruby
If you change the first line of recursive_merge to
merged_hash = merge_to.clone
it works as expected:
recursive_merge(hash_a, hash_b)
-> {:a=>{:b=>{:c=>"d", :x=>"y"}}}
Changing the hash as you move through it is troublesome, you need a "work area" to accumulate your results.
Merge multidimensional array of hash based on hash key and value in ruby
maybe this can help you.
input = [
[
{"id"=>"1","name"=>"a"},
{"id"=>"2","name"=>"b"},
{"id"=>"3","name"=>"c"},
{"id"=>"4","name"=>"d"},
{"id"=>"5","name"=>"e"},
{"id"=>"6","name"=>"f"}
],
[
{"id"=>"3","hoby"=>"AA"},
{"id"=>"3","hoby"=>"BB"},
{"id"=>"1","hoby"=>"CC"},
{"id"=>"1","hoby"=>"DD"},
{"id"=>"4","hoby"=>"EE"}
],
[
{"id"=>"1","language"=>"A"},
{"id"=>"1","language"=>"B"},
{"id"=>"2","language"=>"B"},
{"id"=>"2","language"=>"C"},
{"id"=>"6","language"=>"D"}
]
]
This way you can make your "sort" results.
output = {}
input.flatten.each do |h|
output[h["id"]] = {} unless output[h["id"]]
output[h["id"]].merge!(h)
end
output.values
# => [
# => {"id"=>"1", "name"=>"a", "hoby"=>"DD", "language"=>"B"},
# => {"id"=>"2", "name"=>"b", "language"=>"C"},
# => {"id"=>"3", "name"=>"c", "hoby"=>"BB"},
# => {"id"=>"4", "name"=>"d", "hoby"=>"EE"},
# => {"id"=>"5", "name"=>"e"},
# => {"id"=>"6", "name"=>"f", "language"=>"D"}
# => ]
But the better way is use Hash in input. You can define input like hash and "id" like key so if you generate the data, you dont have problem to sort it.
Someting like this
{
"1" => {"name" => "a", "hoby" => "DD", "language" => "B"}
}
Converting multi dimensional array into hash of hashes in ruby
There are several ways to do that. Here is one. I use the form of Hash#update (aka merge!
) that uses a hash to determine the values of keys that are present in both hashes being merged.
arr =[%w| a 1 2 3 |, %w| b 3 4 5 |, %w| c 12 33 12 |, %w| a 3 5 3 |]
#=> [["a", "1", "2", "3"], ["b", "3", "4", "5"],
# ["c", "12", "33", "12"], ["a", "3", "5", "3"]]
arr.map { |a,b,c,d| { a => { b => { c => d } } } }.
each_with_object({}) { |g,h| h.update(g) { |_k,o,v| o.merge(v) } }
#=> {"a"=>{"1"=>{"2"=>"3"}, "3"=>{"5"=>"3"}},
# "b"=>{"3"=>{"4"=>"5"}},
# "c"=>{"12"=>{"33"=>"12"}}}
See the doc for Hash#update
for a description of the three keys in the block that computes the hash value for keys present in both hashes being merged. The first, _k
, is the common key. I wrote that beginning with an underscore (you'll often see just _
) to signify that it is not used in the block calculation.
The first step is
arr.map { |a,b,c,d| { a => { b => { c => d } } } }
#=> [{"a"=>{"1"=>{"2"=>"3"}}},
# {"b"=>{"3"=>{"4"=>"5"}}},
# {"c"=>{"12"=>{"33"=>"12"}}},
# {"a"=>{"3"=>{"5"=>"3"}}}]
This array of four hashes is then converted to a single hash (represented by the block variable h
, which is initially empty), using update
.
Combine first elements of multidimensional array
Try this:
records.group_by(&:first).map do |id, records_for_id|
{
id: id,
count: records_for_id.sum(&:last)
}
end
Combine first elements of multidimensional array
Try this:
records.group_by(&:first).map do |id, records_for_id|
{
id: id,
count: records_for_id.sum(&:last)
}
end
Ruby - combining/flattening multiple array of hashes on common hash key/value combination
TL;DR
snapshots.each_with_object(Hash.new {|hsh, date| hsh[date] = { "date" => date } })
.with_index do |(snapshot, hsh), i|
snapshot["data"].each {|datum| hsh[datum["date"]]["data#{i}"] = datum["total"] }
end.values
How it works
I'll break it down so you see how each part works. Here's our data (extraneous keys elided for clarity):
snapshots = [
{ "dataSourceID" => "152970",
"data" => [ { "date" => "1455672010", "total" => "817" },
{ "date" => "1455595298", "total" => "40" },
{ "date" => "1455336016", "total" => "555" } ]
}
{ "dataSourceID" => "33151",
"data" => [ { "date" => "1455672010", "total" => "70" },
{ "date" => "1455595298", "total" => "54" },
{ "date" => "1455336016", "total" => "25" } ]
},
{ "dataSourceID" => "52165",
"data" => [ { "date" => "1455672010", "total" => "70" },
{ "date" => "1455595298", "total" => "43212" },
{ "date" => "1455336016", "total" => "55525" } ]
}
]
Most of the magic is here:
result_hash = Hash.new {|hsh, date| hsh[date] = { "date" => date } }
Here we're using the Hash's default proc to automatically initialize new keys in the following way:
result_hash = Hash.new {|hsh, date| hsh[date] = { "date" => date } }
p result_hash["1455672010"]
# => { "date" => "1455672010" }
p result_hash
# => { "1455672010" => { "date" => "1455672010" } }
Simply accessing result_hash[foo]
creates the hash { "date" => foo }
and assigns it to result_hash[foo]
. This enables the following:
result_hash["1455672010"]["data0"] = "817"
p result_hash
# => { "1455672010" => { "date" => "1455672010", "data0" => "817" } }
Magic!
Now suppose we have the following data:
data = [ { "date" => "1455672010", "total" => "817" },
{ "date" => "1455595298", "total" => "40" },
{ "date" => "1455336016", "total" => "555" } ]
Using our magic result_hash
, we can do this:
data.each do |datum|
result_hash[datum["date"]]["data0"] = datum["total"]
end
p result_hash
# => { "1455672010" => { "date" => "1455672010", "data0" => "817" },
# "1455595298" => { "date" => "1455595298", "data0" => "40" },
# "1455336016" => { "date" => "1455336016", "data0" => "555" } }
See where I'm going with this? Here's all of our data, finally:
snapshots = [
{ "dataSourceID" => "152970",
"data" => [ { "date" => "1455672010", "total" => "817" },
{ "date" => "1455595298", "total" => "40" },
{ "date" => "1455336016", "total" => "555" } ]
}
{ "dataSourceID" => "33151",
"data" => [ { "date" => "1455672010", "total" => "70" },
{ "date" => "1455595298", "total" => "54" },
{ "date" => "1455336016", "total" => "25" } ]
},
{ "dataSourceID" => "52165",
"data" => [ { "date" => "1455672010", "total" => "70" },
{ "date" => "1455595298", "total" => "43212" },
{ "date" => "1455336016", "total" => "55525" } ]
}
]
Instead of hard-coding "data0"
, we can iterate over the snapshots
hashes using each_with_index
and build that key ("data0"
, then "data1"
, and so on) for each iteration. Inside that loop we can do exactly what we did above but with the "data"
array from each snapshots
hash:
result_hash = Hash.new {|hsh, date| hsh[date] = { "date" => date } }
snapshots.each_with_index do |snapshot, i|
data_key = "data#{i}"
snapshot["data"].each do |datum|
date = datum["date"]
result_hash[date][data_key] = datum["total"]
end
end
p result_hash.values
# => [ { "date" => "1455672010", "data0" => "817", "data1" => "70", "data2" => "70" },
# { "date" => "1455595298", "data0" => "40", "data1" => "54", "data2" => "43212" },
# { "date" => "1455336016", "data0" => "555", "data1" => "25", "data2" => "55525" } ]
Of course, this can be condensed some, which I've done in TL;DR above.
Related Topics
Redefining a Single Ruby Method on a Single Instance with a Lambda
Using Join Tables in Ruby on Rails
How to Check If a Value Is a Number
What Does the "$" Character Mean in Ruby
Error: SASS Installation for Windows
How to Configure Ruby on Rails with Oracle
How to Count the Number of Records That Have a Unique Value in a Particular Field in Ror
How to Select Every Nth Item in an Array
Finding the Element of a Ruby Array with the Maximum Value for a Particular Attribute
Put a Link in a Flash[:Notice]
In Ruby on Rails, After Send_File Method Delete the File from Server
How to Reference a Function in Ruby
Couldn't Require Openssl in Ruby
How to Know When to "Refresh" My Model Object in Rails