简体   繁体   English

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

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

I have this input: 我有这个输入:

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

I'm trying to get a hash of arrays (which, in turn, are hashes of arrays). 我正在尝试获取数组的哈希(反过来又是数组的哈希)。 I'd like to get this hash: 我想得到这个哈希:

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

This is my code: 这是我的代码:

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
}

The keys of the hash are the unique combinations of values in Column1 and Column2 (Col1 ° Col2), and the values are the relations between Col2 (key of 2nd level hash), Col3, and Col4 (These two as elements of the internal arrays). 哈希键是Column1和Column2中值的唯一组合(Col1 ° Col2),值是Col2(第二级哈希键),Col3和Col4(这两个作为内部数组的元素)之间的关系)。

I almost getting the result, but there is an extra nesting. 我几乎得到了结果,但是有一个额外的嵌套。 I get this result: 我得到这个结果:

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

Please give me some help. 请给我一些帮助。

UPDATE UPDATE

Modification to a shorter code from Cary's suggestion. 从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 UPDATE2

Even after Cary's help I was able to get my final output, I show below why I was trying to get the hash of arrays and inside the arrays another hashes of arrays. 即使在Cary的帮助下,我仍然可以获得最终的输出,但我仍在下面说明了为什么要尝试获取数组的哈希,并在数组内部尝试另一个数组哈希。

This is the output. 这是输出。 Is like organize a book index showing the sections ("Us" and "Pa"), then showing the chapters of each section ( 1 and 2 for "Us" and 1 and 4 for "Pa"). 就像组织书籍索引显示各个部分(“ Us”和“ Pa”),然后显示每个部分的章节(“ Us”分别为1和2,“ Pa”为1和4)。 Then for each chapter show each article and it's related description, example article "3" has the description "D", so "D" is printed next to "3" and article "3" belongs to chapter "1" that belongs to section "Pa". 然后为每个章节显示每个文章及其相关描述,示例文章“ 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

Thanks for great help! 感谢您的大力帮助!

You can fix your code by replacing 您可以通过替换来修复代码

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

with

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

or 要么

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

As you know, it is the value of b after the code has been executed that you want, so you should add b as a final line. 如您所知,它是执行代码后所需的b值,因此应将b添加为最后一行。

A few other observations: 其他一些观察:

  • if r = line.split(",") is changed to r = line.chomp.split(",") the following line is simplified. 如果将r = line.split(",")更改为r = line.chomp.split(",") ,则会简化以下行。
  • a.map { |k,v|... can be replaced with a.each { |k,v|... , which is more appropriate and reads better. 可以将a.map { |k,v|...替换为a.each { |k,v|... ,它更合适并且a.each { |k,v|...更好。
  • a[r[0] + "°" + r[1]]... made my eyes hurt. a[r[0] + "°" + r[1]]...使我的眼睛受伤。 You never need to resort to such hacks. 您无需诉诸于此类骇客。 You could have instead written a[r[0], r[1]]... , deleted m=k.split("°") and replaced the next line with b[k[0]] += [k[1]=> v] . 您可以改写a[r[0], r[1]]... ,删除m=k.split("°")并将下一行替换为b[k[0]] += [k[1]=> v]

Here are two other ways you could do that. 您可以通过以下两种方法来做到这一点。 Both approaches use the method Hash#transform_values , which made its debut in Ruby v2.4. 两种方法都使用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
_

Use Enumerable#group_by 使用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"]]}]}

The steps are as follows. 步骤如下。

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>

When executing the last expression the first value passed to the block and assigned to the block variable is: 当执行最后一个表达式时,传递给该块并分配给该块变量的第一个值是:

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

The block calculation then returns: 块计算然后返回:

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

Use Hash#update 使用Hash#update

This uses the form of Hash#update (aka Hash#merge! ) that employs a block to determine the values of keys that are present in both hashes being merged. 这使用Hash#update的形式(又名Hash#merge! ),该形式采用一个块来确定要合并的两个哈希中存在的键的值。 This form of update is used in two nesting levels. 这种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>

Note the code preceding transform_values returns the following. 注意, transform_values前面的代码返回以下内容。

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

A variant of this method is the following. 下面是此方法的一种变体。

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>

Note the code preceding transform_values returns the following. 注意, 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) converts the values of "Us" and "Pa" (which are hashes) to arrays of the values of those hashes (which are also hashes), namely, transform_values(&:values)"Us""Pa" (即哈希)的值转换为这些哈希(也是哈希)的值的数组,即

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

for the key "Us" and "Us"

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

for "Pa" . "Pa" It is because we want the values of "Us" and "Pa" to be arrays of hashes that we need the somewhat odd expression 这是因为我们希望"Us""Pa"的值是散列数组,所以我们需要一些奇怪的表达式

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

Had we wanted to the values of "Us" and "Pa" to be a single hash we could have written 如果我们希望将"Us""Pa"的值设为一个我们可以编写的哈希值

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

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

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