2012-03-09 6 views
7

Скажем, у меня есть массив как это:Как рассчитать средние значения в объектах, расположенных в массиве?

[ 
    { 
    "player_id"   => 1, 
    "number_of_matches" => 2, 
    "goals"    => 5 
    }, 
    { 
    "player_id"   => 2, 
    "number_of_matches" => 4, 
    "goals"    => 10 
    } 
] 

Я хочу иметь средние голов за матч среди всех игроков, а не в среднем для каждого отдельного игрока, а общий средний.

Я имею в виду делать это с .each и хранить каждое из индивидуальных средних значений, а в конце добавить их все и разделить на количество игроков, которые у меня есть. Тем не менее, я ищу способ Ruby/one-liner сделать это.

+0

Вы можете исправить массив/хэш, так что это на самом деле действует рубин. –

+0

Извините, я получаю JSON, и я сопоставляю его с хешем. Позвольте мне изменить это. – Nobita

+2

Однострочники интересны, но часто переоценены, ИМО. Я думаю, что попросить «элегантное» и «чистое» решение лучше, чем просить однострочный. –

ответ

16

В соответствии с просьбой, один вкладыш:

avg = xs.map { |x| x["goals"].to_f/x["number_of_matches"] }.reduce(:+)/xs.size 

Более читаемый фрагмент:

goals, matches = xs.map { |x| [x["goals"], x["number_of_matches"]] }.transpose 
avg = goals.reduce(:+).to_f/matches.reduce(:+) if goals 
+0

Ницца и чистой. –

+0

-1 ОП запросил один лайнер. – Kyle

+0

Кайл: Для этого в одной строке потребуется повторение кода или неточные результаты. –

0
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: переименованы ключи и блок-арг, чтобы уменьшить количество обугленного. Для тех, кто заботится.

Во-первых, ваши ключи должны использовать =>, если вы собираетесь использовать строки в качестве ключей.

reduce будет перебирать массив и суммировать индивидуальные средние значения для каждого игрока, и, наконец, мы делим этот результат на количество игроков. «0» в скобках - ваш начальный номер для reduce.

+1

'arr.map {| p | p [: goals] .to_f/p [: number_of_matches]} .reduce (: +)/arr.size' будет немного короче (а не переполнять код div). –

+0

Из 93 символов в вашем однострочном пространстве, только 3 являются пробелами, а еще несколько вокруг операторов сделают его более читаемым. –

+0

Niklas: вы сопоставляете, а затем уменьшаете, тем самым повторяя массив дважды, когда требуется только один проход. – Kyle

1

Небольшая модификация ответа tokland.

items.map{|e| e.values_at("goals", "number_of_matches")}.transpose.map{|e| e.inject(:+)}.instance_eval{|goals, matches| goals.to_f/matches} 
+0

Хе-хе, хороший трюк с 'instance_eval' :) Я бы предпочел не видеть это в производственном коде: P –

0

Для того, чтобы строка короче, позволяет переименовывать "number_of_matches" в "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 
Смежные вопросы