2012-03-09 15 views
7

Diciamo che ho un array come questo:Come si calcolano i valori medi negli oggetti che si trovano in una matrice?

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

voglio avere i media gol a partita tra tutti i giocatori, non la media per ogni singolo giocatore, ma la media totale.

Ho in mente di farlo con .each e memorizzare ciascuna delle singole medie, e alla fine aggiungerle tutte e dividere per il numero di giocatori che ho. Tuttavia, sto cercando un modo Ruby/one-liner per farlo.

+0

Si potrebbe voler risolvere l'array/hash in modo che in realtà è valida Ruby. –

+0

Scusa, ricevo un JSON e lo mappo a un hash. Lascia che ti modifichi. – Nobita

+2

Le one-liner sono interessanti, ma spesso sopravvalutate, IMO. Penso che chiedere una soluzione * elegante * e * pulita * sia meglio che chiedere una carta unica. –

risposta

16

come richiesto, una battuta:

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

Un frammento di più leggibile:

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

Bello e pulito. –

+0

-1 OP ha richiesto un solo rivestimento. – Kyle

+0

Kyle: Fare questo in una riga richiede la ripetizione del codice o risultati imprecisi. –

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: chiavi rinominati e args blocco per ridurre conteggio char. Per coloro a cui importa

In primo luogo, le chiavi devono utilizzare => se si utilizzano stringhe come chiavi.

reduce itererà sull'array e somma le singole medie per ciascun giocatore e infine dividiamo il risultato per il numero di giocatori totali. Lo "0" tra parentesi è il tuo numero iniziale per reduce.

+1

'arr.map {| p | p [: goals] .to_f/p [: number_of_matches]} .reduce (: +)/arr.size' sarebbe un po 'più breve (e non esagerare con il codice div). –

+0

Dei 93 caratteri nella tua unica linea, solo 3 sono spazi e altri ancora attorno agli operatori lo renderebbero molto più leggibile. –

+0

Niklas: si sta mappando e quindi riducendo, quindi ripetendo l'array due volte quando è richiesto un solo passaggio. – Kyle

1

Una leggera modifica alla risposta di 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, bel trucco con 'instance_eval' :) Preferisco non vederlo nel codice di produzione, però: P –

0

Per rendere stringa più corta, consente di rinominare "number_of_matches" a "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 
Problemi correlati