对对象数组中的多个属性求和

时间:2020-05-04 15:55:40

标签: arrays ruby

我了解在ruby中,我们可以将像这样的数组中的元素求和

[1,2,3].sum 
[<Object a: 1>, <Object a:2>].sum(&:a)

如何对对象数组中的多个属性求和。例如:

arr = [<Object a:1, b:2, ..., n:10>, <Object a:1, b:2, ..., n:10>, ... <nth Object>]

获取多个属性a,b,... n的总和的最有效方法是什么?

我尝试了以下方法:

sum_a = arr.sum(&:a)
sum_b = arr.sum(&:b)
a, b = 0, 0
arr.each do |obj|
  a += obj.a
  b += obj.b
end

我想知道是否有更好的方法可以解决这个问题。

3 个答案:

答案 0 :(得分:2)

您可以为此创建自己的方法:

def sum_multiple(arr, *keys)
  arr.each_with_object(keys.zip([0] * keys.size).to_h) do |o,obj|
    keys.each {|k| obj[k] += o.public_send(k)}
  end
end

用法:

class A 
  attr_reader :a,:b,:c 

  def initialize(a,b,c)
    @a,@b,@c = a,b,c
  end 
end 

arr = 10.times.map {|i| A.new(i,i*2,i*3)}

sum_multiple(arr,:a)
#=> {:a=>45}
sum_multiple(arr,:a,:b)
#=> {:a=>45, :b=>90}
sum_multiple(arr,:a,:b,:c)
#=> {:a=>45, :b=>90, :c=>135}
"Sum of everything: #{sum_multiple(arr,:a,:b,:c).sum(&:last)}" 
#=> "Sum of everything: 270"

Example

注意::如果这是Rails,则应研究group by和sum的组合并将其放在数据库中。

答案 1 :(得分:1)

如果您只希望对某些键值求和,则可以执行以下操作:

[{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}].flat_map{|h| h.slice(:a, :b).values}.sum

或者,如果您想对所有值求和,

[{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}].flat_map(&:values).sum

对于可能不是散列的对象

arr.sum {|o| [:a, :b, :c].sum{ |key| o.send key} }

答案 2 :(得分:1)

让我们从创建实例数组开始。

class C
  def initialize(a,b,c)
    @a, @b, @c = a, b, c
  end
end

arr = [[1,2,3], [4,5,6], [7,8,9]].map { |a| C.new(*a) }
  #=> [#<C:0x00005a4282c2d980 @a=1, @b=2, @c=3>,
  #    #<C:0x00005a4282c2d5c0 @a=4, @b=5, @c=6>,
  #    #<C:0x00005a4282c2d3b8 @a=7, @b=8, @c=9>] 

然后

ivs = arr.first.instance_variables
  #=> [:@a, :@b, :@c]
arr.map { |i| ivs.map { |v| i.instance_variable_get(v) } }. 
    transpose.
    map(&:sum)
 #=> [12, 15, 18] 

步骤如下。

a = arr.map { |i| ivs.map { |v| i.instance_variable_get(v) } }
  #=> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
b = a.transpose
  #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]] 
c = b.map(&:sum) 
  #=> [12, 15, 18] 

如果需要标签:

ivs = arr.first.instance_variables
ivs.zip(arr.map {|i| ivs.map {|v| i.instance_variable_get(v)}}. 
            transpose.
            map(&:sum)).
    to_h
  #=> {:@a=>12, :@b=>15, :@c=>18}

如果只需要某些实例变量的总和,例如@a@c,则只需更改ivs

ivs = [:@a, :@c]
ivs.zip(arr.map {|i| ivs.map {|v| i.instance_variable_get(v)}}. 
            transpose.
            map(&:sum)).
    to_h
  #=> {:@a=>12, :@c=>18}
相关问题