Reputation: 23713
Let's say I have an array like this:
[
{
"player_id" => 1,
"number_of_matches" => 2,
"goals" => 5
},
{
"player_id" => 2,
"number_of_matches" => 4,
"goals" => 10
}
]
I want to have the average goals per match among all the players, not the average for each individual player, but the total average.
I have in mind doing it with .each
and storing each of the individual averages, and at the end add them all and divide by the number of players I have. However, I am looking for a Ruby/ one-liner way of doing this.
Upvotes: 8
Views: 4803
Reputation: 67850
As requested, a one-liner:
avg = xs.map { |x| x["goals"].to_f / x["number_of_matches"] }.reduce(:+) / xs.size
A more readable snippet:
goals, matches = xs.map { |x| [x["goals"], x["number_of_matches"]] }.transpose
avg = goals.reduce(:+).to_f / matches.reduce(:+) if goals
Upvotes: 17
Reputation: 21791
To make string shorter, lets rename "number_of_matches"
to "matches"
a = [
{"player_id":1 , "matches":2, "goals": 5},
{"player_id":2 , "matches":4, "goals": 10}
]
a.reduce([0,0]){|sum,h|[sum.first+h["goals"],sum.last+h["matches"]]}.reduce{|sum,m|sum.to_f/m}
#=> 2.5
Upvotes: 0
Reputation: 22258
a = [{player_id:1 , match_num:2, goals: 5}, {player_id:2 , match_num:4, goals: 10}]
a.reduce(0){|avg, p| avg += p[:goals].to_f/p[:match_num]}/a.size
Edit: renamed keys and block args to reduce char count. For those who care.
First, your keys need to use =>
if your going to use strings as keys.
reduce
will iterate over the array and sum the individual averages for each player and finally we divide that result by the number of total players. The '0' in the parenthesis is your starting number for reduce
.
Upvotes: 0
Reputation: 168071
A slight modification to tokland's answer.
items.map{|e| e.values_at("goals", "number_of_matches")}.transpose.map{|e| e.inject(:+)}.instance_eval{|goals, matches| goals.to_f/matches}
Upvotes: 1