【问题标题】:Merge hashes with multiple keys/values合并具有多个键/值的哈希
【发布时间】:2013-05-04 14:02:12
【问题描述】:

我有一个哈希数组,我合并了包含特定重复键值的哈希。

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 }

我是 ruby​​ 新手,我不太了解这背后的魔力。我有一个额外的关键和价值来巩固,现在我迷路了。问题的根源是我不明白这一行中的合并是如何发生的: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 },
]

【问题讨论】:

  • 你能给出一个包含 4 个预期输入的样本以及你想要的输出吗?
  • 我已经添加了我想要的输出

标签: ruby-on-rails ruby hash


【解决方案1】:

假设你有:

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 的原始哈希值。

【讨论】:

    【解决方案2】:

    给定

    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 }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-16
      • 2011-06-01
      • 1970-01-01
      • 2014-09-14
      • 2023-03-17
      • 2015-07-26
      • 1970-01-01
      • 2018-02-12
      相关资源
      最近更新 更多