Ruby 分组/总结两个哈希的最有效方法?
我有两个散列,其中包含一些需要聚合的数据。第一个是哪些id(id_1、id_2、id_3、id_4)属于哪个类别(a、b、c)的映射: 第二个哈希值保存给定日期(日期1、日期2、日期3)每个id发生的事件数: 我想要的是得到每个类别(a、b、c)的总事件数。对于上面的示例,结果如下所示:Ruby 分组/总结两个哈希的最有效方法?,ruby,Ruby,我有两个散列,其中包含一些需要聚合的数据。第一个是哪些id(id_1、id_2、id_3、id_4)属于哪个类别(a、b、c)的映射: 第二个哈希值保存给定日期(日期1、日期2、日期3)每个id发生的事件数: 我想要的是得到每个类别(a、b、c)的总事件数。对于上面的示例,结果如下所示: hash_3 = {'a' => (5+6+8+0+6), 'b' => (0+0+1), 'c' => (10+1)} 我的问题是,大约有5000个类别,每个类别通常指向1到3个ID,每个
hash_3 = {'a' => (5+6+8+0+6), 'b' => (0+0+1), 'c' => (10+1)}
我的问题是,大约有5000个类别,每个类别通常指向1到3个ID,每个ID具有30个或更多日期的事件计数。所以这需要相当多的计算。在Ruby中进行分组最有效的方法是什么
更新
这就是我到目前为止所尝试的(花了6-8秒,速度非常慢):
另外,我正在使用Ruby 2.3。理论
5000*3*30
不是很多。Ruby可能最多需要一秒钟来完成这种工作
默认情况下,哈希查找是快速的,您将无法进行太多优化
您可以预先计算hash\u 2\u sum
,不过:
hash_2_sum = {
'id_1' => 5+6+8,
'id_2' => 0+6,
'id_3' => 0+0+1,
'id_4' => 10+1
}
使用hash\u 2\u sum
lookup在hash1
上循环一次,就完成了
代码
您的示例已更新为一些nil
值。您需要使用删除它们,并确保在未找到具有以下内容的元素时,总和为0
:
注
不是很容易读懂
您可以将每个_与_对象一起使用
或数组#to _h
:
result = [1, 2, 3].each_with_object({}) do |i, hash|
hash[i] = i * i
end
#=> {1=>1, 2=>4, 3=>9}
result = [1, 2, 3].map { |i| [i, i * i] }.to_h
#=> {1=>1, 2=>4, 3=>9}
首先,创建一个中间散列,其中包含
散列2
的总和:
hash_4 = hash_2.map{|k, v| [k, v.values.inject(:+)]}.to_h
# => {"id_1"=>19, "id_2"=>6, "id_3"=>1, "id_4"=>11}
然后做最后的总结:
hash_3 = hash_1.map{|k, v| [k, v.map{|k| hash_4[k]}.inject(:+)]}.to_h
# => {"a"=>25, "b"=>1, "c"=>11}
我在手机上写这封信,所以我现在无法测试,但看起来没问题
g = hash_2.each_with_object({}) { |(k,v),g| g[k] = v.values.compact.sum }
hash_3 = hash_1.each_with_object({}) { |(k,v),h| h[k] = g.values_at(*v).sum }
不要害怕尝试一些东西,因为有450000个计数;事实上这并不多。您可能会惊讶于解决方案的运行速度。(如果没有,我们将在这里帮助优化它!)请参阅更新A在实际应用程序中您是否知道数组的大致大小
hash_1.values.uniq
?对于大多数类别,大约5000-10000个,只有一个ID对应。对于一些类别2-3 ID相对应。对于这些数字,我看不到比答案中包含的更有效的方法。Ruby 2.4可以,OP在2.4之前的Ruby版本中使用Ruby 2.3(除非也使用Rails,它已经有一段时间了sum
),将sum
替换为reduce(:+)
。请注意,OP还更新了他的输入数据。现在值可以是nil
。@Eric,谢谢。我刚才注意到了。当我看到另一条评论时,我知道它是关于什么的,它来自谁。:-)不仅仅是注入(:+)
?@CarySwoveland:[]注入(:+)#=>nil
,[]注入(0,:+)#=>0
{}.tap do |res|
hash_1.each do |cat, ids|
res[cat] = total_event_per_ids(ids)
end
end
result = [1, 2, 3].each_with_object({}) do |i, hash|
hash[i] = i * i
end
#=> {1=>1, 2=>4, 3=>9}
result = [1, 2, 3].map { |i| [i, i * i] }.to_h
#=> {1=>1, 2=>4, 3=>9}
hash_4 = hash_2.map{|k, v| [k, v.values.inject(:+)]}.to_h
# => {"id_1"=>19, "id_2"=>6, "id_3"=>1, "id_4"=>11}
hash_3 = hash_1.map{|k, v| [k, v.map{|k| hash_4[k]}.inject(:+)]}.to_h
# => {"a"=>25, "b"=>1, "c"=>11}
g = hash_2.each_with_object({}) { |(k,v),g| g[k] = v.values.compact.sum }
hash_3 = hash_1.each_with_object({}) { |(k,v),h| h[k] = g.values_at(*v).sum }