2012-03-09 12 views
7

Powiedzmy mam tablicę tak:Jak obliczyć wartości średnie w obiektach znajdujących się w tablicy?

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

chcę mieć średnią goli na mecz spośród wszystkich graczy, a nie średnią dla każdego gracza, ale łączna średnia.

Mam na myśli robienie tego z .each i zapisywanie każdej z indywidualnych średnich, a na koniec dodawanie ich wszystkich i dzielenie przez liczbę graczy, których posiadam. Jednak szukam sposobu, aby to zrobić w Ruby/one-liner.

+0

Może chcesz naprawić tablicy/mieszania tak, że jest to rzeczywiście ważny Ruby. –

+0

Przepraszam, dostaję JSON i ja mapuję to do mieszania. Pozwól mi to edytować. – Nobita

+2

One-liners są interesujące, ale często przereklamowane, IMO. Myślę, że prośba o * eleganckie * i * czyste * rozwiązanie jest lepsze niż prośba o jeden liniowiec. –

Odpowiedz

16

Zgodnie z wnioskiem, one-liner:

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

Bardziej czytelny fragment:

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

Ładne i czyste. –

+0

-1 OP poprosił o jeden liniowiec. – Kyle

+0

Kyle: Wykonanie tej czynności w jednym wierszu wymaga powtórzenia kodu lub niedokładnych wyników. –

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: przemianowany klucze i args blokowe zmniejszyć char licznik. Dla tych, którzy dbają.

Po pierwsze, twoje klucze muszą używać =>, jeśli zamierzasz używać ciągów jako kluczy.

reduce spowoduje powtórzenie całej tablicy i zsumowanie indywidualnych średnich dla każdego gracza, a następnie podzielimy ten wynik przez liczbę wszystkich graczy. "0" w nawiasie to twój numer początkowy dla reduce.

+1

'arr.map {| p | p [: cele] .to_f/p [: number_of_matches]} .reduce (: +)/arr.size' będzie nieco krótszy (i nie przepełni kodu div). –

+0

Spośród 93 znaków w twojej linijce, tylko 3 to spacje, a kilka innych wokół operatorów sprawiłoby, że byłaby znacznie bardziej czytelna. –

+0

Niklas: mapujesz, a następnie redukujesz, co powoduje dwukrotne powtarzanie tablicy, gdy wymagane jest tylko jedno przejście. – Kyle

1

Niewielka modyfikacja odpowiedzi na 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

Heh, miła sztuczka z 'instance_eval' :) Wolałbym tego nie widzieć w kodzie produkcyjnym: P –

0

Aby łańcuch krótszy, pozwala zmienić nazwę "number_of_matches" do "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 
Powiązane problemy