【问题标题】:Extra nested second level hash of arrays in RubyRuby 中额外嵌套的二级数组散列
【发布时间】:2018-10-16 11:48:22
【问题描述】:

我有这个输入:

Us,1,1,F
Us,1,2,O
Us,2,1,N 
Pa,1,1,S
Pa,1,3, D
Pa,1,5,H
Pa,4,7,K

我正在尝试获取数组的哈希值(反过来,它是数组的哈希值)。我想得到这个哈希:

b = {
  "Us" => [
    {"1" => [["1", "F"], ["2", "O"]]},
    {"2" => [["1", "N"]]}
  ],
  "Pa" => [
    {"1" => [["1", "S"], ["3", "D"], ["5", "H"]]},
    {"4" => [["7", "K"]]}
  ]
}

这是我的代码:

a = Hash.new{|hsh, key| hsh[key] = []}
b = Hash.new{|hsh, key| hsh[key] = []}
File.readlines('file.txt').each do |line|
  r = line.split(",")
  a[r[0] + "°" + r[1]].push [r[2], r[3].strip] # I load hash "a" here
end

a.map{|k, v|
  m=k.split("°")
  b[m[0]].push [m[1]=> v] # I load hash "b" here
}

hash的key是Column1和Column2中值的唯一组合(Col1°Col2),value是Col2(二级hash的key)、Col3和Col4之间的关系(这两个为内部数组的元素)。

我几乎得到了结果,但是有一个额外的嵌套。我得到了这个结果:

b = {
  "Us"=>[
    [{"1"=>[["1", "F"], ["2", "O"]]}],
    [{"2"=>[["1", "N"]]}]
  ],
  "Pa"=>[
    [{"1"=>[["1", "S"], ["3", "D"], ["5", "H"]]}],
    [{"4"=>[["7", "K"]]}]
  ]
}

请给我一些帮助。

更新

根据 Cary 的建议修改为更短的代码。

a = Hash.new{|hsh, key| hsh[key] = []}
b = Hash.new{|hsh, key| hsh[key] = []}

File.readlines('input').each do |line|
  r = line.chomp.split(",")
  a[[r[0], r[1]]].push [r[2], r[3]]
end

a.each{|k, v|
  b[k[0]].concat [k[1] => v]    
}

更新2

即使在 Cary 的帮助下,我也能够获得最终输出,我在下面展示了为什么我试图获取数组的哈希值以及在数组内部另一个数组的哈希值。

这是输出。就像组织一个显示章节(“Us”和“Pa”)的书籍索引,然后显示每个章节的章节(“Us”为 1 和 2,“Pa”为 1 和 4)。然后对于每章显示每篇文章及其相关描述,示例文章“3”具有描述“D”,因此“D”打印在“3”旁边,文章“3”属于属于章节的“1”章“帕”。

 Us 
    ......1
    ..............1.......F
    ..............2.......O
    ......2 
    ..............1.......N
   Pa
    ......1
    ..............1.......S
    ..............3.......D
    ..............5.......H
    ......4
    ..............7.......K

感谢您的大力帮助!

【问题讨论】:

    标签: arrays ruby hash nested


    【解决方案1】:

    您可以通过替换来修复您的代码

    b[m[0]].push [m[1]=>v]
    

    b[m[0]] += [m[1]=> v]
    

    b[m[0]].concat [m[1]=> v]
    

    如你所知,你想要的是代码执行后b的值,所以你应该在最后一行添加b

    其他一些观察:

    • 如果将r = line.split(",") 更改为r = line.chomp.split(","),则以下行被简化。
    • a.map { |k,v|...可以换成a.each { |k,v|...,这样更合适,也更好读。
    • a[r[0] + "°" + r[1]]... 看得我眼睛疼。你永远不需要求助于这样的黑客。您可以改写 a[r[0], r[1]]...,删除 m=k.split("°") 并将下一行替换为 b[k[0]] += [k[1]=> v]

    这里还有另外两种方法可以做到这一点。这两种方法都使用方法Hash#transform_values,它在Ruby v2.4 中首次亮相。

    str =<<_
    Us,1,1,F
    Us,1,2,O
    Us,2,1,N 
    Pa,1,1,S
    Pa,1,3,D
    Pa,1,5,H
    Pa,4,7,K
    _
    

    使用Enumerable#group_by

    str.lines.
        map { |line| line.chomp.split(',') }.
        group_by(&:shift).
        transform_values { |arr| arr.group_by(&:shift).map { |k,v| { k=>v } } }
      #=> {"Us"=>[{"1"=>[["1", "F"], ["2", "O"]]}, {"2"=>[["1", "N "]]}],
      #    "Pa"=>[{"1"=>[["1", "S"], ["3", " D"], ["5", "H"]]}, {"4"=>[["7", "K"]]}]}
    

    步骤如下。

    a = str.lines
      #=> ["Us,1,1,F\n", "Us,1,2,O\n", "Us,2,1,N \n",
      #    "Pa,1,1,S\n", "Pa,1,3, D\n", "Pa,1,5,H\n", "Pa,4,7,K\n"]
    b = a.map { |line| line.chomp.split(',') }
      #=> [["Us", "1", "1", "F"], ["Us", "1", "2", "O"], ["Us", "2", "1", "N "],
      #    ["Pa", "1", "1", "S"], ["Pa", "1", "3", " D"], ["Pa", "1", "5", "H"],
      #    ["Pa", "4", "7", "K"]]
    c = b.group_by(&:shift)
      #=> {"Us"=>[["1", "1", "F"], ["1", "2", "O"], ["2", "1", "N "]],
      #    "Pa"=>[["1", "1", "S"], ["1", "3", " D"], ["1", "5", "H"],
      #           ["4", "7", "K"]]}
    c.transform_values { |arr| arr.group_by(&:shift).map { |k,v| { k=>v } } }
      #=> <the return value shown above>
    

    执行最后一个表达式时,传递给块并分配给块变量的第一个值是:

    arr = [["1", "1", "F"], ["1", "2", "O"], ["2", "1", "N "]]
    

    块计算然后返回:

    d = arr.group_by(&:shift)
      #=> {"1"=>[["1", "F"], ["2", "O"]], "2"=>[["1", "N "]]}
    d.map { |k,v| { k=>v } }
      #=> [{"1"=>[["1", "F"], ["2", "O"]]}, {"2"=>[["1", "N "]]}]
    

    使用Hash#update

    这使用Hash#update(又名Hash#merge!)的形式,它使用一个块来确定在被合并的两个散列中存在的键的值。这种update 形式用于两个嵌套级别。

    str.lines.each_with_object({}) do |line, h|
      s0, s1, s2, s3 = line.chomp.split(',')
      h.update(s0=>{ s1=>[[s2, s3]] }) do |_0,oh0,nh0|
        oh0.merge(nh0) { |_1,oh1,nh1| oh1+nh1 }
      end
    end.transform_values { |h| h.map { |k,v| { k=>v } } }
      #=> <the return value shown above>
    

    注意transform_values 前面的代码返回以下内容。

    {"Us"=>{"1"=>[["1", "F"], ["2", "O"]], "2"=>[["1", "N"]]},
     "Pa"=>{"1"=>[["1", "S"], ["3", " D"], ["5", "H"]], "4"=>[["7", "K"]]}}
    

    此方法的一个变体如下。

    str.lines.each_with_object({}) do |line, h|
      s1, s2, s3, s4 = line.chomp.split(',')
      h.update(s1=>{ s2=>{ s2=>[[s3, s4]] } }) do |_0,oh0,nh0|
        oh0.merge(nh0) do |_1,oh1,nh1|
          oh1.merge(nh1) { |_2,oh2,nh2| oh2+nh2  }
        end
      end
    end.transform_values(&:values)
      #=> <the return value shown above>
    

    注意transform_values 前面的代码返回以下内容。

    h = {"Us"=>{"1"=>{"1"=>[["1", "F"], ["2", "O"]]}, "2"=>{"2"=>[["1", "N "]]}},
         "Pa"=>{"1"=>{"1"=>[["1", "S"], ["3", " D"], ["5", "H"]]}, "4"=>{"4"=>[["7", "K"]]}}}
    

    transform_values(&amp;:values)"Us""Pa"(它们是散列)的值转换为这些散列值(也是散列)的数组,即,

    [{"1"=>[["1", "F"], ["2", "O"]]}, {"2"=>[["1", "N "]]}]
    

    为密钥"Us"

    [{"1"=>[["1", "S"], ["3", " D"], ["5", "H"]]}, {"4"=>[["7", "K"]]}]
    

    "Pa"。因为我们希望"Us""Pa" 的值是哈希数组,所以我们需要有点奇怪的表达式

    s1=>{ s2=>{ s2=>[[s3, s4]] } }
    

    如果我们希望 "Us""Pa" 的值成为我们可以编写的单个哈希

    s1=>{ s2=>[[s3, s4]] }
    

    【讨论】:

      猜你喜欢
      • 2013-01-30
      • 1970-01-01
      • 2018-02-10
      • 1970-01-01
      • 2013-03-14
      • 2011-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多