Reputation: 286
I have an array of "votes" set up as [ID, Rating] inside another array
[["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "4"],
["1250", "5"],
["1250", "5"],
["1252", "2"],
["1252", "5"],
["1252", "4"],
["1252", "3"],
["1252", "5"],
["1252", "4"],
["1252", "4"],
["1254", "5"],
["1254", "4"],
["1254", "4"],
["1257", "5"],
["1257", "5"],
["1257", "4"],
["1257", "5"],
...]
There are multiples of x that I want to merge and keep all y's pertaining to that x accessible. Basically I have to average all votes (y) for a particular id (x) and am unsure how to do that. In addition these votes (y) have to be "weighted" by different amounts later, so I think keeping access to them would be helpful down the road.
Frankly I don't even know what this is called so don't know what to look up :/ I tried merging, pushing y's onto array[x], and some complicated 'for unique x do |y|'. Just stumped on how to handle this problem.
End goal could be something like this:
[["1250", ["5", "5", "5", "4", "5", "5"]],
["1252", ["2", "5", "4", "3", "5", "4", "4"]],
["1254", ["5", "4", "4"]],
["1257", ["5", "5", "4", "5"]],
...]
Upvotes: 1
Views: 90
Reputation: 110675
Assuming your array is ordered by the first element of each (two-element array) element of votes, as it is in the example, you could make use of Enumerable#slice_when which we were given in v.2.2:
votes.slice_when { |(v1,_),(v2,_)| v1 != v2 }
.each_with_object({}) { |a,h| h[a.first.first] = a.map(&:last) }
#=> {"1250"=>["5", "5", "5", "5", "4", "5", "5"],
# "1252"=>["2", "5", "4", "3", "5", "4", "4"],
# "1254"=>["5", "4", "4"],
# "1257"=>["5", "5", "4", "5"]}
where:
votes =
[["1250", "5"],
["1250", "5"],
...
["1257", "4"],
["1257", "5"]]
Upvotes: 1
Reputation: 7685
One short solution.
my_array = [ .... ]
my_array.group_by(&:first).map { |k,v| [k, v.map { |_,y| [y] }.reduce(:+)] }
It uses the Enumerable methods group_by()
, map()
, reduce()
.
EDIT: Additional notes
With a small adaption the solution above also accumulates the values of the ys. I guessed it was the primary intension of the question but wasn't.
my_array.group_by(&:first).map { |k,v| [k, v.map { |_,y| y.to_i }.reduce(:+)] }
Upvotes: 3
Reputation: 80065
[["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "4"],
["1250", "5"],
["1250", "5"],
["1252", "2"],
["1252", "5"],
["1252", "4"],
["1252", "3"],
["1252", "5"],
["1252", "4"],
["1252", "4"],
["1254", "5"],
["1254", "4"],
["1254", "4"],
["1257", "5"],
["1257", "5"],
["1257", "4"],
["1257", "5"]]
hsh = Hash.new{|h,k| h[k] = []}
# hsh stores the key with an empty array if it does not "know" a key
votes.each_with_object(hsh){|(id, vote), h| h[id] << vote}
# add vote to the array when hsh "knows" the key.
p hsh
# =>{"1250"=>["5", "5", "5", "5", "4", "5", "5"], "1252"=>["2", "5", "4", "3", "5", "4", "4"]...}
Upvotes: 1
Reputation: 76
You could build a Hash where the ID is the key and the value could be an array of the ratings:
table = Hash.new()
list.each do |id_rating_pair|
id = id_rating_pair[0]to_sym
rating = id_rating_pair[1].to_i
if !table.has_key?( id )
table[id] = Array.new()
end
table[id].push( rating )
end
Now with this table you can perform your statistics.
I know it's not short, but it is clear and can be revised to suit your needs.
Upvotes: 0
Reputation: 2965
Maybe you can do that with a hash.
votes= [["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "5"],
["1250", "4"],
["1250", "5"],
["1250", "5"],
["1252", "2"],
["1252", "5"],
["1252", "4"],
["1252", "3"],
["1252", "5"],
["1252", "4"],
["1252", "4"],
["1254", "5"],
["1254", "4"],
["1254", "4"],
["1257", "5"],
["1257", "5"],
["1257", "4"],
["1257", "5"]]
resume={}
votes.each do |vote|
resume[vote[0]]=[] unless resume.include?(vote[0])
resume[vote[0]] << vote[1]
end
puts resume.to_s
and then you can do anything you want with that hash.
Upvotes: 1
Reputation: 106802
I would do something like this:
array.group_by(&:first).map { |k, v| [k, v.map(&:last)] }
Upvotes: 1