繁体   English   中英

Ruby中数组的额外嵌套二级哈希

[英]Extra nested second level hash of arrays in Ruby

我有这个输入:

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
}

哈希键是Column1和Column2中值的唯一组合(Col1 ° Col2),值是Col2(第二级哈希键),Col3和Col4(这两个作为内部数组的元素)之间的关系)。

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

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

请给我一些帮助。

UPDATE

从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]    
}

UPDATE2

即使在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

感谢您的大力帮助!

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

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.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(&: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]] }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM