我有一个哈希数组,我合并了包含特定重复键值的哈希值。
combined_keywords = new_array_of_hashes.each_with_object(Hash.new(0)){|oh, newh|
newh[oh[:keyword]] += oh[:total_value].to_f
}
这会创建一个如下所示的哈希数组:
{ :ACTUAL_KEYWORD => ACTUAL_TOTAL_VALUE }
我是红宝石的新手,我不太明白这背后的魔力。我有一个额外的关键和价值来巩固,现在我迷路了。问题的根源是我不明白在这一行中如何进行合并:newh[oh[:keyword]] += oh[:total_value].to_f
我试了这个没有运气:
combined_keywords = new_array_of_hashes.each_with_object(Hash.new(0)){|oh, newh|
newh[oh[:keyword]] += oh[:total_value].to_f
newh[oh[:keyword]] += oh[:revenue_per_transaction].to_f
}
我真的需要一系列相似的哈希值:
{ :keyword => "ACTUAL_KEYWORD", :total_value => ACTUAL_TOTAL_VALUE, :revenue_per_transaction => ACTUAL_REVENUE }
修改
输入
new_array_of_hashes = [
{ keyword: 'foo', total_value: 1, revenue_per_transaction: 5 },
{ keyword: 'bar', total_value: 2, revenue_per_transaction: 4 },
{ keyword: 'bar', total_value: 4, revenue_per_transaction: 4 },
{ keyword: 'foo', total_value: 3, revenue_per_transaction: 5 },
]
期望输出
combined_keywords = [
{ keyword: 'foo', total_value: 4, revenue_per_transaction: 10 },
{ keyword: 'bar', total_value: 6, revenue_per_transaction: 8 },
]
答案 0 :(得分:3)
假设你有:
new_array_of_hashes = [
{ keyword: 'foo', total_value: 1 },
{ keyword: 'bar', total_value: 2 },
{ keyword: 'bar', total_value: 4 },
{ keyword: 'foo', total_value: 3 },
]
现在我们将逐步完成您的代码:
combined_keywords = new_array_of_hashes.each_with_object(Hash.new(0)){|oh, newh|
newh[oh[:keyword]] += oh[:total_value].to_f
}
这将循环遍历数组中的每个哈希。我们还设置了一个新哈希,如果我们访问一个不存在的密钥,则返回0
:
# Pass 1
oh = { keyword: 'foo', total_value: 1 }
newh = {}
newh[ oh[:keyword] ] #=> newh['foo'] This key doesn't exist and returns 0
oh[:total_value].to_f #=> 1.to_f => 1.0
newh[oh[:keyword]] += oh[:total_value].to_f
#=> newh['foo'] = newh['foo'] + oh[:total_value].to_f
#=> newh['foo'] = 0 + 1.0
# Pass 2
oh = { keyword: 'bar', total_value: 2 }
newh = { 'foo' => 1.0 }
newh[ oh[:keyword] ] #=> newh['bar'] This key doesn't exist and returns 0
oh[:total_value].to_f #=> 2.to_f => 2.0
newh[oh[:keyword]] += oh[:total_value].to_f
#=> newh['bar'] = newh['bar'] + oh[:total_value].to_f
#=> newh['bar'] = 0 + 2.0
现在,由于我们有接下来两次迭代的密钥,我们正常访问:
# Pass 3
oh = { keyword: 'bar', total_value: 4 }
newh = { 'foo' => 1.0, 'bar' => 2.0 }
newh[ oh[:keyword] ] #=> newh['bar'] This key now exists and returns 2.0
oh[:total_value].to_f #=> 4.to_f => 4.0
newh[oh[:keyword]] += oh[:total_value].to_f
#=> newh['bar'] = newh['bar'] + oh[:total_value].to_f
#=> newh['bar'] = 2.0 + 4.0
# Pass 4
oh = { keyword: 'foo', total_value: 3 }
newh = { 'foo' => 1.0, 'bar' => 6.0 }
newh[ oh[:keyword] ] #=> newh['foo'] This key now exists and returns 1.0
oh[:total_value].to_f #=> 3.to_f => 3.0
newh[oh[:keyword]] += oh[:total_value].to_f
#=> newh['foo'] = newh['foo'] + oh[:total_value].to_f
#=> newh['foo'] = 1.0 + 3.0
当块返回时,它将返回newh
;这就是each_with_object
的工作原理。
如您所见,返回的是表单的哈希:
{ 'foo' => 4.0, 'bar' => 6.0 }
所以这只是一个组合数组,其中新键是存储的:keyword
对象,值是总和。
基于您的新哈希表格
{
keyword: "ACTUAL_KEYWORD",
total_value: ACTUAL_TOTAL_VALUE,
revenue_per_transaction: ACTUAL_REVENUE
}
这种格式没有多大意义。由于哈希只有键:值对。您可能需要具有子哈希的哈希值,或者运行两次循环。一次为:total_value
,一次为:revenue_per_transaction
。它真的取决于你想要的最终对象。
修改强>
根据您的新预期输入和输出,您可以使用:
sum_keys = [:total_value, :revenue_per_transaction]
new_array_of_hashes.group_by{ |h| h[:keyword] }
.map{ |keyword, related|
tmp = {keyword: keyword}
tmp.merge! Hash[sum_keys.zip Array.new(sum_keys.size, 0)]
related.reduce(tmp){ |summed, h|
sum_keys.each{ |key| summed[key] += h[key] }
summed
}
}
#=> [
# { keyword: 'foo', total_value: 4, revenue_per_transaction: 10 },
# { keyword: 'bar', total_value: 6, revenue_per_transaction: 8 },
#]
有点乱。我可能会重构map
调用对它自己的帮助方法的影响。我向reduce
提供起始值的原因是因为它会改变new_array_of_hashes
的原始哈希值。
答案 1 :(得分:0)
鉴于
foos = [ { :key => 'Foo', :value => 1, :revenue => 2 },
{ :key => 'Foo', :value => 4, :revenue => 8 } ]
你可以这样做
foos.each_with_object(Hash.new(0)) do |foo_hash, new_hash|
new_hash[:keyword] = foo_hash[:key]
new_hash[:total_value] += foo_hash[:value]
new_hash[:total_revenue] += foo_hash[:revenue]
end
因此each_with_object允许您将参数传递给可枚举的.each块。在这种情况下,您传递的是Hash.new(0)。 0参数是一种设置默认哈希值的方法,因此您不必在循环中将值显式归零为零,只需向右递增。 +=
只是简写。因此a += b
相当于a = a + b
。
关于循环的笨重之处是它在每次传递时设置new_hash [:keyword]值。你可以使用if new_hash[:keyword] == 0
(因为它的首字母缩写为零)但这只是一个绑带。问题在于原始的哈希结构。如果:key aways等于'Foo'那么'Foo'是多余的。如果它不总是'Foo'那么这个循环不是很有用。
上面的循环产生
{ :keyword => 'Foo', :total_value => 5, :total_revenue => 10 }