Merging Multi-Dimensional Hashes in Ruby

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



Leave a reply



Submit