Ruby按字段汇总对象数组的方式

问题描述 投票:0回答:2

我有一个对象数组,我想按field1分组,并按字段2求和。一个例子是具有product场和title场的类price。在一系列产品中,我有多个价格不同的手套,以及不同价格的多个帽子。我想要一个具有不同标题的数组,汇总同一标题下的所有价格。

有一个明显的解决方案,迭代数组并使用哈希,但我想知道是否有一种“红宝石方式”做这样的事情?我已经看过很多例子,其中Ruby有一些独特的功能,适用于某些场景,并且是一个Ruby新手我很好奇。

谢谢

arrays ruby
2个回答
2
投票

有一个方法transform_values added in ruby 2.4或如果你require 'active_support/all',你可以这样做:

products = [
  {type: "hat", price: 1, name: "fedora"},
  {type: "hat", price: 2, name: "sombrero"},
  {type: "glove", price: 3, name: "mitten"},
  {type: "glove", price: 4, name: "wool"}
]

result = products
  .group_by { |product| product[:type] }
  .transform_values { |vals| vals.sum { |val| val[:price] } }
# => {"hat"=>3, "glove"=>7}

1
投票

根据问题询问你的数据是什么样的,我有点不清楚,所以我最终得到了这个:

Product = Struct.new(:title, :price)

products = [
  Product.new("big hat", 1),
  Product.new("big hat", 2),
  Product.new("small hat", 3),
  Product.new("small hat", 4),
  Product.new("mens glove", 8),
  Product.new("mens glove", 9),
  Product.new("kids glove", 1),
  Product.new("kids glove", 2)
]

鉴于这些数据,这就是我如何建立一个包含给定标题的所有价格总和的数据结构:

sum_by_title = products.inject({}) do |sums, product|
  if sums[product.title]
    sums[product.title] += product.price
  else
    sums[product.title] = product.price
  end
  sums
end

这会产生:

{"big hat"=>3, "small hat"=>7, "mens glove"=>17, "kids glove"=>3}

解释:

Ruby inject获取初始值并将其作为“备忘录”传递给迭代块。在这里,{}是初始值。来自块的返回值作为备忘录传递到下一次迭代。

product.title用作散列键,运行总和存储在散列值中。 if语句是必要的,因为第一次遇到产品标题时,该标题的存储值为nil且不能递增。


由于hash default value构造函数的隐藏魔法,我可能不会发送此代码,但是可以在没有if语句的情况下编写相同的代码:

sum_by_title = products.inject(Hash.new { 0 }) do |sums, product|
  sums[product.title] += product.price
  sums
end

希望你像我一样享受Ruby!

© www.soinside.com 2019 - 2024. All rights reserved.