I have an array of hashes ( edited ):
data = [
{id: 1, name: "Amy", win: 1, defeat: 0},
{id: 1, name: "Amy", win: 1, defeat: 3},
{id: 2, name: "Carl", win: 0, defeat: 1},
{id: 2, name: "Carl", win: 2, defeat: 1}
]
How can I group or merge into something like this using the key "name" as reference:
data = [
{id: 1, name: "Amy", win: 2, defeat: 3},
{id: 2, name: "Carl", win: 2, defeat: 2}
]
edited I forgot to mention that a I have ID too that can't be added.
Here is my try
#!/usr/bin/env ruby
data = [
{"name"=> "Amy", "win" => 1, "defeat" => 0},
{"name"=> "Amy", "win" => 1, "defeat" => 3},
{"name"=> "Carl", "win" => 0, "defeat" => 1},
{"name"=> "Carl", "win" => 2, "defeat" => 1}
]
merged_hash = data.group_by { |h| h['name'] }.map do |_,val|
val.inject do |h1,h2|
h1.merge(h2) do |k,o,n|
k == 'name' ? o : o + n
end
end
end
merged_hash
# => [{"name"=>"Amy", "win"=>2, "defeat"=>3},
# {"name"=>"Carl", "win"=>2, "defeat"=>2}]
Answer to the edited post :-
#!/usr/bin/env ruby
data = [
{id: 1, name: "Amy", win: 1, defeat: 0},
{id: 1, name: "Amy", win: 1, defeat: 3},
{id: 2, name: "Carl", win: 0, defeat: 1},
{id: 2, name: "Carl", win: 2, defeat: 1}
]
merged_hash = data.group_by { |h| h.values_at(:name, :id) }.map do |_,val|
val.inject do |h1,h2|
h1.merge(h2) do |k,o,n|
%i(id name).include?(k) ? o : o + n
end
end
end
merged_hash
# => [{:id=>1, :name=>"Amy", :win=>2, :defeat=>3},
# {:id=>2, :name=>"Carl", :win=>2, :defeat=>2}]
You can do it in one pass with each_with_object
and a Hash-memo with an appropriate default. For example:
data.each_with_object(Hash.new { |h, k| h[k] = { :id => k.first, :name => k.last, :win => 0, :defeat => 0 } }) do |h, m|
k = h.values_at(:id, :name)
m[k][:win ] += h[:win ]
m[k][:defeat] += h[:defeat]
end.values
The basic trick is to cache the results indexed by an appropriate key ( [ h[:id], h[:name] ]
in this case) and use the values to store what you're after. The default proc on the m
Hash autovivifies cached values and then you can apply simple summations during iteration. And a final values
call to unwrap the cache.
Good place where you can use group_by
result = []
data.group_by{|d| d[:id]}.each do {|name, records|
win = 0
defeat = 0
records.each do |r|
win += r[:win]
defeat += r[:defeat]
end
f = records.first
results << {:id => f[:id], :name => f[:name], :win => win, :defeat => defeat}
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.