【问题标题】:Create new hashes grouped by common key value in Ruby在 Ruby 中创建按公共键值分组的新哈希
【发布时间】:2013-10-05 02:31:24
【问题描述】:

假设我有一个在 Ruby 中看起来像这样的哈希:

{ :ie0 => "Hi",    :ex0 => "Hey",       :eg0 => "Howdy", 
  :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"}

有什么好方法可以把它变成类似的东西:

{ "0" => 
    { 
        "ie" => "Hi", "ex" => "Hey", "eg" => "Howdy"
    },
  "1" => 
    {
        "ie" => "Hello", "ex" => "Greetings", "eg" => "Good day"
    }
}

【问题讨论】:

  • 对于初学者,请尝试:a.group_by { |e| e[0][/\d/] }

标签: ruby hash


【解决方案1】:

您要求一种好的方式来做,所以答案是:您或您的同事可以在六个月后理解和维护的方式。

首先,您需要一个Hash with autovivification,因为您正在创建一个嵌套的哈希结构。这是一种非常有用的编码模式,可以简化您的应用程序代码:

# Copied & pasted from the question
old_hash = { :ie0 => "Hi", :ex0 => "Hey", :eg0 => "Howdy", :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"}

# Auto-vivify
new_hash = Hash.new { |h, k| h[k] = { } }

然后,您可以以这种简单的方式遍历现有的键,分解每个键的部分,并使用它们将值保存在新的哈希中:

old_hash.each_pair do |key, value|
  key =~ /^(..)(.)$/               # Make a regex group for each string to parse
  new_hash[$2][$1] = value         # The regex groups become the new hash keys
end

puts new_hash

我得到这个输出:

{"0"=>{"ie"=>"Hi", "ex"=>"Hey", "eg"=>"Howdy"}, "1"=>{"ie"=>"Hello", "ex"=>"Greetings", "eg"=>"Good day"}}

【讨论】:

  • 是的,我得到了同样的东西。虽然不知道自动复活!
  • 非常适合我,Ruby 2.0.0。我将编辑以添加完整的代码。
  • 啊,只是没有puts new_hash。太棒了,谢谢!
  • 有趣的方法。我可以看到以这种方式使用正则表达式如何在涉及迭代的操作中有很多潜在的应用。
  • 是的,@CarySwoveland,当我提前知道字符串是“匹配”时,我一直使用它。正则表达式组是解析信息的好方法,便于稍后返回并了解代码在做什么。
【解决方案2】:

它不漂亮,但它有效:

input = { :ie0 => "Hi",    :ex0 => "Hey",       :eg0 => "Howdy",
          :ie1 => "Hello", :ex1 => "Greetings", :eg1 => "Good day"}

output = input.inject({}) do |result, item|
  item[0] =~ /(?<key>[^\d]+)(?<index>\d+)/
  key, index = $1, $2
  value = item[1]

  result[index] ||= {}
  result[index].merge! { key => value }
  result
end

puts output

【讨论】:

  • 我必须这样做 result[index].merge!(key =&gt; value) 但它有效。哇谢谢!
【解决方案3】:
require 'awesome_print'

hsh = {
           :ie0 => "Hi",
           :ex0 => "Hey",
           :eg0 => "Howdy",
           :ie1 => "Hello",
           :ex1 => "Greetings",
           :eg1 => "Good day"
      }

new_hsh = hsh.each_with_object(Hash.new { |h, k| h[k] = { } }) do |(k,v),h|
       h[k[-1]].merge!({k[0..-2] => v})
end
ap new_hsh

输出(格式为awesome_print

{
    "0" => {
        "ie" => "Hi",
        "ex" => "Hey",
        "eg" => "Howdy"
    },
    "1" => {
        "ie" => "Hello",
        "ex" => "Greetings",
        "eg" => "Good day"
    }
}

【讨论】:

  • 很好,奥雅纳! (评论已结束,但还不够长..)
  • @CarySwoveland 非常感谢! :)
【解决方案4】:

编辑:向支持者道歉,我现在宁愿放弃这个答案(但我自己不会反对)。我会让它留在这里,因为它的历史意义(当然还有 10 分)。在它的许多缺点中,它只允许结果中的单个数字键。我提交了另一个我更喜欢的解决方案。

h = {ie0: "Hi", ex0: "Hey", eg0: "Howdy", ie1: "Hello", ex1: "Greetings", eg1: "Good day"}

n = h.keys.map {|k| k.to_s[0..-2]}.uniq.size # => 3

Hash[*h.to_a.each_slice(n).to_a.map {|s| [s.first.first.to_s[-1], Hash[*s.map {|v| [v.first.to_s[0..-2], v.last]}.flatten]]}.flatten]

【讨论】:

    【解决方案5】:

    这是一种使用@Mason 的建议(在对问题的评论中)使用group_by 的方法。

    h = {ie0: "Hi", ex0: "Hey", eg0: "Howdy", ie999: "Hello", ex999: "Greetings", eg999: "Good day"}
    

    编辑:新的和改进的(?):

    p Hash[h.group_by {|e| e[0][/\d+/]}.map {|k,v| [k, Hash[v.map {|a| [a.first.to_s[/[^\d]+/], a.last]}]]}]
    

    以前我有:

    hh = h.group_by { |e| e[0][/\d+/] }
    hh.each_key {|k| hh[k] = Hash[*[hh[k].map {|v| [v.first.to_s[/[^\d]+/], v.last]}].flatten]}
    

    欢迎提出建议。我发现这是一个有趣的问题,并喜欢阅读其他答案,我发现这些答案多种多样且具有创新性。

    【讨论】:

    • 不知道 each_key。不错。
    猜你喜欢
    • 1970-01-01
    • 2022-01-10
    • 2016-05-28
    • 2015-04-19
    • 1970-01-01
    • 2020-07-02
    • 2015-11-18
    • 1970-01-01
    • 2020-10-09
    相关资源
    最近更新 更多