Rails - 如何在单独的对象中一起添加值

时间:2017-04-01 20:29:06

标签: ruby-on-rails ruby

您有一个包含键值引用的对象数组。

[{booking_ref: 'w578383', foo: 'bar', price1: 500, price2: 30],
{booking_ref: 'w578383', foo: 'bar', price1: 600, price2: 40},
{booking_ref: 'r123523', foo: 'bar', price1: 699, price2: 4}]

我想:

  • 按键值参考(booking_ref)分组对象
  • 通过这些分组和每个对象只能一起添加财务价值(price1和price2到其他预订的price1和price2)
  • 将分组折叠回包含键值引用的对象数组。现在就是:

    [{booking_ref: 'w578383', foo: 'bar', price1: 1100, price2: 70},
    {booking_ref: 'r123523', foo: 'bar', price1: 699, price2: 4}]
    

我在想:

objects.group_by(&:booking_ref).each {|group|
  group.merge {|key, value1, value2| value1 + value2 if key == price1 || price2}
}

这是否有效?如果是这样我如何将它们退回到group_by状态?

4 个答案:

答案 0 :(得分:1)

使用哈希对象,您可以计算总和并将它们合并回每个组中的第一个哈希:

bookings = [
{booking_ref: 'w578383', foo: 'bar', price1: 500, price2: 30},
{booking_ref: 'w578383', foo: 'bar', price1: 600, price2: 40},
{booking_ref: 'r123523', foo: 'bar', price1: 699, price2: 4}
]

grouped_bookings = bookings.group_by{ |h| h[:booking_ref] }.map do |ref, hs|
  sums = hs.each_with_object(Hash.new(0)) do |h, sum|
    %i(price1 price2).each do |price|
      sum[price] += h[price].to_i
    end
  end
  hs.first.merge(sums)
end

p grouped_bookings
# [{:booking_ref=>"w578383", :foo=>"bar", :price1=>1100, :price2=>70},
# {:booking_ref=>"r123523", :foo=>"bar", :price1=>699, :price2=>4}]

答案 1 :(得分:1)

每当您可以使用Enumerable#group_by时,您可以使用某种形式的Hash#mergeHash#update(又名merge!),反之亦然。其他人使用group_by,所以这里是哈希合并答案。

让变量bookings等于您的哈希数组,您可以编写以下内容。

keys_to_aggregate = [:price1, :price2]

bookings.each_with_object({}) { |g,h| h.update(g[:booking_ref]=>g) { |_,o,n|
  keys_to_aggregate.reduce(o) { |f,k| f.merge(k=>o[k] + n[k]) } } }.values
  #=> [{:booking_ref=>"w578383", :foo=>"bar", :price1=>1100, :price2=>70},
  #    {:booking_ref=>"r123523", :foo=>"bar", :price1=>699, :price2=>4}] 

请注意,在评估表达式末尾的Hash#values之前,我们有以下内容。

bookings.each_with_object({}) { |g,h| h.update(g[:booking_ref]=>g) { |_,o,n|
  keys_to_aggregate.reduce(o) { |f,k| f.merge(k=>o[k] + n[k]) } } }
  #=> {"w578383"=>{:booking_ref=>"w578383", :foo=>"bar", :price1=>1100, :price2=>70},
  #    "r123523"=>{:booking_ref=>"r123523", :foo=>"bar", :price1=>699, :price2=>4}}

这使用Hash#update的形式,它使用一个块来确定合并的两个哈希中存在的键的值。有关详细信息,请参阅文档,特别是值确定块的三个变量(kon)的定义。 (我已将_替换为k [密钥],表示它未在块计算中使用。)

答案 2 :(得分:0)

首先,group_by没问题,然后使用map迭代哈希值,然后使用inject

将列表中的值减少为总和
objects.group_by(&:booking_ref)
       .map{|ref, list| {booking_ref: ref, 
                        price1:  => list.inject(0){|sum,h| sum + h.price1},
                        price2: => list.inject(0){|sum,h| sum + h.price2}
                        }
           }

答案 3 :(得分:0)

在这里采用面向对象的方法,你可以看起来非常整洁和优雅。关键是在对象上定义+方法。完整示例如下:

class Booking
  attr_accessor :booking_ref, :foo, :price1, :price2

  def initialize(params={})
    params.each { |key, value| send "#{key}=", value }
  end

  # add up prices, and return new Booking object
  def +(other)
    new_obj = self.dup
    new_obj.price1 += other.price1
    new_obj.price2 += other.price2
    new_obj
  end
end

# set up example bookings
bookings = [
  Booking.new(booking_ref: 'w578383', foo: 'bar', price1: 500, price2: 30),
  Booking.new(booking_ref: 'w578383', foo: 'bar', price1: 600, price2: 40),
  Booking.new(booking_ref: 'r123523', foo: 'bar', price1: 699, price2: 4)
]


# grouping becomes very simple - potentially a one-liner
bookings.group_by(&:booking_ref).map { |_, values| values.reduce(&:+) }
# => [
        #<Booking:... @booking_ref="w578383", @foo="bar", @price1=1100, @price2=70>,
        #<Booking:... @booking_ref="r123523", @foo="bar", @price1=699, @price2=4>
     ]