Дополнительный вложенный хэш второго уровня массивов в Ruby - PullRequest
0 голосов
/ 06 мая 2018

У меня есть этот вход:

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
}

Ключами хеша являются уникальные комбинации значений в столбце 1 и столбце 2 (Col1 ° Col2), а значениями являются отношения между Col2 (ключом хэша 2-го уровня), Col3 и Col4 (эти два как элементы внутренних массивов).

Я почти получаю результат, но есть дополнительное вложение. Я получаю этот результат:

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

Пожалуйста, помогите мне.

UPDATE

Изменение более короткого кода по предложению Кэри.

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

Даже после помощи Кэри я смог получить свой окончательный результат, ниже я покажу, почему я пытался получить хэш массивов, а внутри массивов - еще один хэш массивов.

Это вывод. Это как организовать книжный указатель с указанием разделов («Нас» и «Па»), а затем с указанием глав каждого раздела (1 и 2 для «Нас» и 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

Спасибо за большую помощь!

1 Ответ

0 голосов
/ 06 мая 2018

Вы можете исправить свой код, заменив

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(&: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]] }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...