2012-03-09 13 views
7

Digamos que tiene una matriz de esta manera:¿Cómo calculo los valores promedio en objetos ubicados en una matriz?

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

Quiero tener las metas promedio por partido entre todos los jugadores, no el promedio para cada jugador individual, pero el promedio total.

Tengo en mente hacerlo con .each y almacenar cada uno de los promedios individuales, y al final agregarlos a todos y dividir por la cantidad de jugadores que tengo. Sin embargo, estoy buscando una manera Ruby/one-liner de hacer esto.

+0

Es posible que desee fijar la matriz/hachís por lo que es realmente válida Rubí. –

+0

Lo siento, obtengo un JSON y lo asigno a un hash. Déjame editar eso. – Nobita

+2

One-liners es interesante, pero a menudo sobrevalorado, IMO. Creo que pedir una solución * elegante * y * limpia * es mejor que pedir un proyecto de una sola línea. –

Respuesta

16

Conforme a lo solicitado, una sola línea:

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

Un fragmento más legible:

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

Agradable y limpio. –

+0

-1 OP pidió un trazador de líneas. – Kyle

+0

Kyle: Hacer esto en una línea requerirá la repetición del código o resultados inexactos. –

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 

Editar: teclas de nombre y argumentos de bloque para reducir el conteo de carbón. Para aquellos a quienes les importa

Primero, sus claves necesitan usar => si va a utilizar cadenas como claves.

reduce iterarán sobre la matriz y sumarán los promedios individuales para cada jugador y finalmente dividiremos ese resultado entre la cantidad total de jugadores. El '0' entre paréntesis es el número inicial para reduce.

+1

'arr.map {| p | p [: goals] .to_f/p [: number_of_matches]} .reduce (: +)/arr.size' sería un poco más corto (y no desbordará el código div). –

+0

De los 93 caracteres en su línea única, solo 3 son espacios, y algunos más alrededor de los operadores lo harían mucho más legible. –

+0

Niklas: está mapeando y luego reduciendo, iterando sobre la matriz dos veces cuando solo se requiere una pasada. – Kyle

1

Una pequeña modificación en la respuesta de 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, buen truco con 'instance_eval' :) Preferiría no ver eso en el código de producción, aunque: P –

0

Haciendo cadena más corta, permite cambiar el nombre "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 
Cuestiones relacionadas