rails - Cómo sumar las propiedades de los objetos dentro de una matriz en Ruby
ruby create array (7)
Aquí algunos puntos de referencia interesantes
array = Array.new(1000) { OpenStruct.new(property: rand(1000)) }
Benchmark.ips do |x|
x.report(''map.sum'') { array.map(&:property).sum }
x.report(''inject(0)'') { array.inject(0) { |sum, x| sum + x.property } }
x.compare!
end
Y resultados
Calculating -------------------------------------
map.sum 249.000 i/100ms
inject(0) 268.000 i/100ms
-------------------------------------------------
map.sum 2.947k (± 5.1%) i/s - 14.691k
inject(0) 3.089k (± 5.4%) i/s - 15.544k
Comparison:
inject(0): 3088.9 i/s
map.sum: 2947.5 i/s - 1.05x slower
Como puedes ver, inyecta un poco más rápido
Entiendo que para sumar elementos de matriz en Ruby se puede usar el método de inyección, es decir,
array = [1,2,3,4,5];
puts array.inject(0, &:+)
Pero, ¿cómo puedo sumar las propiedades de los objetos dentro de una matriz de objetos, por ejemplo?
Hay una variedad de objetos y cada objeto tiene una propiedad "efectivo", por ejemplo. Entonces quiero sumar sus saldos de efectivo en un total. Algo como...
array.cash.inject(0, &:+) # (but this doesn''t work)
Me doy cuenta de que probablemente podría hacer una nueva matriz compuesta solo por el efectivo de la propiedad y sumar esto, ¡pero estoy buscando un método más limpio si es posible!
La forma más concisa:
array.map(&:cash).sum
Si el conjunto resultante del mapa tiene elementos nulos:
array.map(&:cash).compact.sum
No hay necesidad de usar inicial en inyección y la operación más puede ser más corta
array.map(&:cash).inject(:+)
Si el valor inicial para la suma es 0, entonces la suma sola es idéntica a la inyección:
array.map(&:cash).sum
Y preferiría la versión de bloque:
array.sum { |a| a.cash }
Debido a que el Proc del símbolo a menudo es demasiado limitado (sin parámetros, etc.).
(Necesita Active_Support)
También puedes probar:
array.sum(&:cash)
Es un atajo para el negocio de las inyecciones y me parece más legible.
http://api.rubyonrails.org/classes/Enumerable.html
#reduce
toma un bloque (el &:+
es un atajo para crear un bloque / proc que hace +
). Esta es una forma de hacer lo que quieres:
array.reduce(0) { |sum, obj| sum + obj.cash }
array.map(&:cash).inject(0, &:+)
o
array.inject(0){|sum,e| sum + e.cash }