【问题标题】:Manipulate array of hashes into grouped hashes with arrays将散列数组操作为带有数组的分组散列
【发布时间】:2017-05-28 22:58:07
【问题描述】:

我有一个哈希数组:

[
    {
        "June" => { "A" => { 3 => 48.4 } }
    },
    {
        "January" => { "C" => { 2 => 88.0} }
    },
    {
        "January"=> { "B" => { 2 => 44.0} }
    },
    {
        "January"=> { "C" => { 4 => 48.8} }
    }
]

我需要将每个相似的哈希键分组到一个由后续值组成的数组中,如下所示:

{
    "June" => [{ "A" => [{ 3 => 48.4 }]] },
    "January" => [
        { "B" => [{ 2 => 44.0}],
        { "C" => [{ 2 => 88.0}, { 4 => 48.8}],
        ] }
}

我正在寻找一种对这些元素进行分组的有效方法。谁能帮我掌握这个哈希值?

我试图避免循环遍历基本数组并手动分组。我希望map(或其他一些可枚举的方法)可以提供我想要的东西。当我使用reduce(Hash.new, :merge) 时,它很接近,但它使用了每个月键的最后一个哈希,而不是将它添加到数组中。

【问题讨论】:

  • 在你想要构造的哈希中,键"January"的值不应该是[{ "B" => [{ 2 => 44.0}], "C" => [{ 2 => 88.0}, { 4 => 48.8}]吗?如果是这样,是否有任何理由要将每个散列包含在一个数组中?将以下内容作为"January" 的值会不会更好:{ "B"=>{ 2=>44.0 }, "C"=>{ 2=>88.0, 4=>48.8 } }

标签: arrays ruby hash


【解决方案1】:

注意:在对问题有了更清晰的理解后,我添加了以下内容。我的原始答案如下。

这是 OP 的哈希数组,稍作修改。

arr = [{ "June"   =>{ "A"=>{ 3=>48.4 } } },
       { "January"=>{ "C"=>{ 2=>88.0 } } },
       { "January"=>{ "B"=>{ "D"=>{ 2=>44.0 } } } },
       { "January"=>{ "C"=>{ 2=>10.0 } } },
       { "January"=>{ "C"=>{ 4=>48.8 } } }]

要构造的散列如下所示。

{ "June"   =>[{ "A"=>[{ 3=>48.4 }] }],
  "January"=>[{ "B"=>[{ "D"=>[{ 2=>44.0 }] }] }],
                "C"=>[{ 2=>98.0, 4=>48.8 }] }] }

注意88.0 + 10.0 #=> 98.02=>98.0 中。

观察arr 中的所有数组都包含一个元素,即散列。在这种情况下,这些数组没有任何用处。因此,我建议改为构造以下哈希:

{ "June"   =>{ "A"=>{ 3=>48.4 } },
  "January"=>{ "B"=>{ "D"=>{ 2=>44.0 } } },
               "C"=>{ 2=>98.0, 4=>48.8 } } }

这可以通过以下递归方法产生。

def recurse(arr)
  arr.map(&:flatten).
      group_by(&:first).
      each_with_object({}) do |(k,v),h|
        o = v.map(&:last)
        h.update(k=>o.first.is_a?(Hash) ? recurse(o) : o.sum )
      end
 end

 recurse(arr)
   #=> {"June"=>{"A"=>{3=>48.4}},
   #    "January"=>{"C"=>{2=>98.0, 4=>48.8}, "B"=>{"D"=>{2=>44.0}}}}

(原答案如下)

这里有两种获取所需哈希的方法。我假设 arr 是您的哈希数组。

#1 使用Hash::new 的形式接受一个块

arr.each_with_object(Hash.new { |h,k| h[k] = [] }) do |g,h|
  k, v = g.to_a.first
  h[k] << v
end
  # => {"June"=>[{"A"=>{3=>48.4}}],
  #     "January"=>[{"C"=>{2=>88.0}}, {"B"=>{2=>44.0}}, {"C"=>{4=>48.8}}]}

#2 使用Enumerable#group_by

arr.map(&:first).
    group_by(&:first).
    tap { |h| h.keys.each { |k| h[k] = h[k].map(&:last) } }
  # => {"June"=>[{"A"=>{3=>48.4}}],
  #     "January"=>[{"C"=>{2=>88.0}}, {"B"=>{2=>44.0}}, {"C"=>{4=>48.8}}]}

步骤如下。

a = arr.map(&:first)
  #=> [["June", {"A"=>{3=>48.4}}], ["January", {"C"=>{2=>88.0}}],
  #    ["January", {"B"=>{2=>44.0}}], ["January", {"C"=>{4=>48.8}}]]
b = a.group_by(&:first)
  #=> {"June"=>[["June", {"A"=>{3=>48.4}}]],
  #    "January"=>[["January", {"C"=>{2=>88.0}}], ["January", {"B"=>{2=>44.0}}],
  #                ["January", {"C"=>{4=>48.8}}]]}
c = b.tap { |h| h.keys.each { |k| h[k] = h[k].map(&:last) } }
  #=> {"June"=>[{"A"=>{3=>48.4}}],
  #    "January"=>[{"C"=>{2=>88.0}}, {"B"=>{2=>44.0}}, {"C"=>{=>48.8}}]}

让我详细说明最后一步。在tap 的块中,我们计算以下内容。

h = b
d = h.keys
  #=> ["June", "January"]

d 的第一个元素被传递给each 的块,并且块变量被分配给该元素。

k = d.first
  #=> "June"

块计算如下。

e = h[k]
  #=> [["June", {"A"=>{3=>48.4}}]]
f = e.map(&:last)
  #=> [{"A"=>{3=>48.4}}]
h[k] = f
  #=> [{"A"=>{3=>48.4}}]
b #=> {"June"=>[{"A"=>{3=>48.4}}],
  #    "January"=>[["January", {"C"=>{2=>88.0}}],
  #                ["January", {"B"=>{2=>44.0}}],
  #                ["January", {"C"=>{4=>48.8}}]]}

接下来,d[1](“一月”)被传递给each的块并执行类似的计算。

我可以写而不是使用Object#tap

h = arr.map(&:first).
    group_by(&:first)
h.keys.each { |k| h[k] = h[k].map(&:last) }
h

tap 只是避免创建局部变量h 以及需要有一个等于h 的最后一行。

【讨论】:

  • 尝试了第二种方法 - 它有效,但它从我的代码中删除了第二层密钥:... "January"=>[{2=>88.0}, {2=>44.0}, { 4=>48.8}]。 (但如此接近)。除此之外 - 它非常适合我正在寻找的东西......我会尝试第一个选项。
  • 第一种方法(每个块)的结果与第二种方法相同......非常接近完美......知道为什么要删除第二层吗?
  • 除非它在我的代码中删除它的其他东西 - 这是在首先生成哈希的一组链式地图等上(这就是为什么首选第二种技术的原因)。跨度>
  • 哎呀!我误读了这个问题。请在评论中告诉我您已阅读此内容。然后我将删除我的答案,修复它,然后取消删除。
  • 在这种情况下,我将添加一点。对这些值求和是什么意思?你的意思是你想总结[48.4, 88.0, 44.0, 48.8]的元素?如果是这样,arr.map { |h| h.to_a.first.last.values.first.values.first }.sum #=&gt; 229.2。要查看这里发生了什么,请执行arr.map { |h| h.to_a },然后执行arr.map { |h| h.to_a.first },然后执行arr.map { |h| h.to_a.first.last } 等等。
猜你喜欢
  • 1970-01-01
  • 2015-07-01
  • 2010-12-04
  • 2015-06-12
  • 2012-06-12
  • 2015-12-15
  • 2020-12-23
  • 2023-04-10
  • 1970-01-01
相关资源
最近更新 更多