【问题标题】:Sum multiple attributes in array of objects对对象数组中的多个属性求和
【发布时间】:2020-05-04 15:55:40
【问题描述】:

我知道在 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

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

【问题讨论】:

    标签: arrays ruby


    【解决方案1】:

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

    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 的组合并将其放入数据库中。

    【讨论】:

    • 这里的A类是怎么定义的?
    • @lacostenycoder 虽然类的定义并不是那么重要,因为方法 #a#b#c 可能是因为它确实有助于再现性我已经添加了定义
    【解决方案2】:

    如果你只想总结一些你可以做的关键值:

    [{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} }
    

    【讨论】:

    • 我认为 OP 正在寻找更像 a.reduce {|memo,h| memo.merge(h) { |_,o,n| o + n }}
    • 但并不是每个对象都有合并方法。
    • @HazhirDerakhshi 并非每个对象都有值方法
    • @engineersmnky 没错。
    • @lacostenycoder 是的,这有点不清楚,因为这 “如何对对象数组中的多个属性进行求和” 可以有多种解释,包括每次传递的临时总和,但代码示例似乎更像是每个属性的总和,无论哪种方式都为努力+1。
    【解决方案3】:

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

    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}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-10-14
      • 1970-01-01
      • 2019-11-16
      相关资源
      最近更新 更多