我有一个ActiveRecord关系,看起来像这样:
[
{
timestamp: Tue, 02 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
organization_id: 1,
all_sales: 10,
direct_sales: 7,
referred_sales: 3,
},
{
timestamp: Wed, 03 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
organization_id: 1,
all_sales: 17,
direct_sales: 8,
referred_sales: 9,
},
{
timestamp: Thu, 04 Oct 2018 00:00:00 PDT -07:00,
user_id: 3,
all_sales: 3,
direct_sales: 3,
referred_sales: 0,
}
]
我想做的是为与销售有关的所有密钥创建一个“和”(出于我们此处的目的,我不需要timestamp
,user_id
或{{1} },所以基本上,我想以这样的结尾:
organization_id
是否有一种优雅的红宝石色方式?我可以轻松地为每个销售类别创建一组变量,并在迭代原始关系时增加它们,但是我想看看社区中是否有更清洁的方法。实际上,这些散列中的每个散列都具有超过3个相关的键,因此,我担心这种方法会很快变得混乱。
编辑:我还在SO上检查了类似问题的其他答案(例如:Better way to sum values in an array of hashes),但理想情况下,我不会重复那么多次。
答案 0 :(得分:5)
由于您是从ActiveRecord关系开始的,因此可以使用pluck用SQL计算所有总和,并让它返回一个包含总数的数组:
SalesModel.pluck('SUM(all_sales)', 'SUM(direct_sales)', 'SUM(referred_sales)')
#=> [30, 18, 12]
答案 1 :(得分:4)
这将起作用:
arr.each_with_object({}) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] = hash[sym].to_i + obj[sym]
end
end
这是一次迭代,您可以将嵌套循环写成3条不同的行,但是我认为这样比较干净。
注意:在获取to_i
的先前值的同时调用hash[sym]
,最初的值为nil
和nil.to_i == 0
。另外,您可以将所有未知计数初始化为0,如下所示:
arr.each_with_object(Hash.new(0)) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] += obj[sym]
end
end
答案 2 :(得分:2)
或将功能性方法与reduce
和merge
方法一起使用:
keys = %i{all_sales direct_sales referred_sales}
total_sales = items.map {|item| item.select{|key, _| keys.include?(key)}}
.reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}
# total_sales
# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}
感谢@Johan Wentholt,对于Ruby 2.5.0或更高版本,几乎没有更清晰的出价方法
items.map {|item| item.slice(:all_sales, :direct_sales, :referred_sales)}
.reduce({}) {|all, item| all.merge(item) {|_, sum, value| sum + value}}
# => {:all_sales=>30, :direct_sales=>18, :referred_sales=>12}
答案 3 :(得分:1)
使用合并和归约功能
value = arr.reduce do |h1, h2|
h1.merge(h2) do |k, v1, v2|
[:all_sales, :direct_sales, :referred_sales].include?(k) ? (v1 + v2) : nil
end
end.reject {|_, v| v.nil?}
p value
答案 4 :(得分:1)
更多详细的其他选项(可以呈现为更DRY和更常规):
result = { all_sales: (array.sum{ |e| e[:all_sales] }), direct_sales: (array.sum{ |e| e[:direct_sales] }), referred_sales: (array.sum{ |e| e[:referred_sales] }) }
或:
result = array.each.with_object(Hash.new(0)) do |h, obj|
obj[:all_sales] += h[:all_sales]
obj[:direct_sales] += h[:direct_sales]
obj[:referred_sales] += h[:referred_sales]
end
要更加干燥和通用,请从所需键的数组开始
keys = [:all_sales, :direct_sales, :referred_sales]
第一个成为
keys.map.with_object(Hash.new) { |k, obj| obj[k] = array.sum { |e| e[k] } }
第二个:
array.each.with_object(Hash.new(0)) { |h, obj| keys.each { |k| obj[k] += h[k] } }