Merging 3 Array/Hashes Into 1 and Preserve The Values

I’m currently building a mini stat page and tracking some users stats
that I would like to combine into a large table for an overview. I
currently have the following…

@new_users = [{“May Sat 03”=>5}, {“May Fri 02”=>1}, {“May Thu
01”=>4}]
@new_entries = [{“May Sat 03”=>2}, {“May Fri 02”=>3}, {“May Thu
01”=>4}]
@new_winners = [{“May Sat 03”=>3}, {“May Fri 02”=>4}, {“May Thu
01”=>4}]

I would like to generate one master array that I can iterate using a
table. My thinking is I would want to have one master array that looks
like this.

@master_list = [ {“May Sat 03”=> [5,2,3]}, {“May Sat 02”=> [1,3,4]},
{“May Sat 01”=> [4,4,4]} ]

or should it look might look like this

@master_list = [{“May Sat 03”=>
[“new_users”=>“5”,“new_entries”=>“2”,“new_winners”=>“3”]},
{“May Sat 03”=>
[“new_users”=>“5”,“new_entries”=>“2”,“new_winners”=>“3”]},
{“May Sat 03”=>
[“new_users”=>“5”,“new_entries”=>“2”,“new_winners”=>“3”]}
]

I would think this first option seems the easiest to produce?

Is there a way in Ruby or in Rails to tell each array to find the
exact same index/key (which are the dates) and merge it’s values? I
have been playing around with Array.find_all and Array.inject but
nothing seems to be clicking on how I would generate the final
@master_list

After some researching online I found 1/2 a solution.
(The example code was found on Hash#collate - Ruby - Ruby-Forum).

What you could do is this…

@master_list = @new_users.update(@new_entries) {|key, *values|
values }

Returns

{“May Sat 03”=>[5, 2], “May Fri 02”=>[1, 3], “May Thu 01”=>[4]}

Only issue is I can’t merge the third array(@new_winners) into
@master_list.

Hi –

On Mon, 5 May 2008, newbie wrote:

01"=>4}]
@master_list = [{“May Sat 03”=>
exact same index/key (which are the dates) and merge it’s values? I
have been playing around with Array.find_all and Array.inject but
nothing seems to be clicking on how I would generate the final
@master_list

Here’s one way:

arrays = [@new_users, @new_entries, @new_winners]
master_list = arrays.transpose.map do |array|
hash = Hash.new {|h,k| h[k] = []}
array.each do |stat|
stat.each do |date,n|
hash[date] << n
end
end
hash
end

The transpose operation gets all of the ones with matching dates
together, which then makes it easier to go through them and pluck out
the values. The Hash.new formula gives you a hash whose default
behavior for an unknown key is to create the key and initialize it to
an empty array.

David


Rails training from David A. Black and Ruby Power and Light:
INTRO TO RAILS June 9-12 Berlin
ADVANCING WITH RAILS June 16-19 Berlin
INTRO TO RAILS June 24-27 London (Skills Matter)
See http://www.rubypal.com for details and updates!

Hi David,

Thanks for the speedy response and the tip on the using “transpose”.
I’m going to further read up on that.

One additional question.If one of your arrays doesn’t have matching
key (for example a date) like the other two might have, does that
change the solution?

For example if I tried to transpose the following:

@new_users = [{“May Sat 03”=>5}, {“May Fri 02”=>1}, {“May Thu
01”=>4}]
@new_entries = [{“May Sat 03”=>2}, {“May Fri 02”=>3}, {“May Thu
01”=>4}]
@new_winners = [{“May Sat 03”=>3}, {“May Fri 02”=>4}]

@new_winners may become problematic because I have a list that is not
exactly the same as the other two.

Seems to me you are putting redundant information into your data. You
know the first half is a date and the second half is a result.
Make a hash with the date as a symbol and the result as a value.

@new_users = {:May_Sat_10=>6 , :May_Fri_09=>1, :May_Mon_05=>0}
@new_entries = {:May_Sat_10=>2, :May_Fri_09=>1, :May_Mon_05=>1}
@new_winners = {:May_Sat_10=>3, :May_Fri_09=>1, :May_Mon_05=>2}

def add_to_hash(my_hash, combined_hash)
my_hash.inject(combined_hash) do |new_hash, (key, value)|
(new_hash[key] ||= []) << value
new_hash
end
end

cool_hash = {}
add_to_hash(@new_users, cool_hash)
add_to_hash(@new_entries , cool_hash)
add_to_hash(@new_winners , cool_hash)

puts “cool Hash”
p cool_hash

I have been working on this and soon realized that I should have made
my keys using a :symbol instead of the actual data. So I have started
from the beginning and modified some details

My Array of data

@new_users = [{:date=>“May Sat 10”, :results=>6}, {:date=>“May Fri
09”, :results=>1}, {:date=>“May Mon 05”, :results=>0}]
@new_entries = [{:date=>“May Sat 10”, :results=>2}, {:date=>“May Fri
09”, :results=>1}, {:date=>“May Mon 05”, :results=>1}]
@new_winners = [{:date=>“May Sat 10”, :results=>3}, {:date=>“May Fri
09”, :results=>1}, {:date=>“May Mon 05”, :results=>2}]

The way of merging them into a master_list

This is where I’m currently stuck and trying out stuff. I’m playing
around with David’s code but I’m not getting the result I need.

My end result should look like this

master_list = [{:date=> “May Sat 10”, :results=>[6,2,3]},{:date=> “May
Fri 09”, :results=>[1,1,1]},{:date=> “May Mon 05”, :results=>[0,1,2]}]

My View

master_list.sort_by { |line_item| line_item[:date] }.each do |
line_item|
puts “#{line_item[:date]}, #{line_item[:results][0]},
#{line_item[:results][1]}, #{line_item[:results][2]}”
end

If you David or anyone else on the forum can help out again, that
would be much appreciated

Thanks for your solution. I tried it but I’m getting errors. The
errors are getting generated in the def add_to_hash method your
proposing.

I’m just curious why wouldn’t my current proposal of using symbols be
correct and not redundant? So far I have read that “…symbols are
useful whenever you’re going to be re-using a word over and over to
represent something…”. Am I misusing the way a symbol should be used
in my proposal?

Disregard the error issue. I just found that I had some odd invisible
characters in my code. I guess it’s not a good idea to copy and paste
directly. So your solution works fine.

I still would like to understand the issue around my proposed way of
using symbols.

Symbols are great, and there is nothing wrong with what your are
doing, I am just saying that you repeat :date and :result for every
record. You could just have a hash with the date as the hash key
(:May_Sat_10) and the result as a hash value rather than a two sets, a
key (:date) with a value of a date and second key(:result) with a
numeric value. Having :date and :result in every record isn’t really
necessary, but you might have a need that I don’t know about. It’s the
same information, just that one format is less verbose.

@new_users = [{:date=>“May Sat 10”, :results=>6}, {:date=>“May Fri
09”, :results=>1}, {:date=>“May Mon 05”, :results=>0}]
vs
@new_users = {:May_Sat_10=>6 , :May_Fri_09=>1, :May_Mon_05=>0}

Ruby onward and good luck.

I like your example the only issue for me is I need to output
everything out into a table. The idea of having…
May_Sat_10
May_Fri_09

is not easy on the eyes. I also found that its difficult to actual
sort with your solution.

Maybe it’s not as pretty as it needs to be, but I need to convert
those 3 arrays into one using this type of formatting (see example)

@new_users = [{:date=>“May Sat 10”, :results=>6}, {:date=>“May Fri
09”, :results=>1}]
@new_entries = [{:date=>“May Sat 10”, :results=>2}, {:date=>“May Fri
09”, :results=>3}]
@new_winners = [{:date=>“May Sat 10”, :results=>3}, {:date=>“May Fri
09”, :results=>4}]

that way, when I output this to a table I’ll be able to use this and
sort it very easily

output

master_list.sort_by { |line_item| line_item[:date] }.each do |
line_item|
puts “#{line_item[:date]}, #{line_item[:results][0]},
#{line_item[:results][1]}, #{line_item[:results][2]}”
end

Thanks for your input so far