Объединить хэши массивов на основе похожих позиций с Ruby - PullRequest
0 голосов
/ 30 апреля 2018

У меня есть следующие два хэша с массивами в качестве значений.

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

Я пытаюсь объединить хэши, чтобы получить финал, как это:

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

Я нашел следующий код с merge и попытался применить его к моей проблеме, но получил ошибку:

a.merge(b) {|key, a_val, b_val| a_val.merge b_val }
# >> NoMethodError: undefined method `merge' for [["1", ["1", "2"]], ["2", ["1"]]]:Array

Я даже получил ошибку с a + b:

a + b
# >> NoMethodError: undefined method `+' for #<Hash:0x0000060078e460>

<<<< ОБНОВЛЕНИЕ >>>>

Спасибо и Кэри, и Тэдману. Вне исходного вопроса я показываю входной файл, который у меня есть, и вывод, который я пытаюсь получить. Я показываю для того, чтобы вы поняли, почему Я сгенерировал 2 хэша таким образом. В выходных данных я создаю блоки, в которых отцы - это уникальные значения столбца 1, ниже дочерних (уникальные значения в столбце 2, связанные со столбцом 1). Столбец 3 - это дочерние элементы, принадлежащие значению в столбце col2, а столбец 4 - текстовое содержимое, связанное с столбцом col3.

Вероятно, хеш "c" легче с самого начала генерировать.

Это мой входной файл

Main,Stage1,Stage2,Description
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

Это вывод, который я почти получаю.

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

Тогда я смог создать этот код, но, как говорит Тадман, мне нужно изменить порядок, как я это делаю, чтобы упростить вещи, так как Я использую 4 хэша. После того, как я создал хэш «a» и «b», я застрял, так как мне нужен был уникальный хеш для итерации и возможности печати в структуре вывода, показанной выше.

Мой код перед публикацией вопроса

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

File.foreach('file.txt').with_index do
    |line, line_num|

    if line_num > 0
        r = line.split(",")

        X[r[0] + "°" + r[1]].push r[2]
        Y[r[0] + "°" + r[1]].push r[3].strip
    end
end

X.each{ |k,v|
    lbs = k.split("°")
    a[lbs[0]].push [ lbs[1], v] #Here I generate hash a
}

Y.each{ |k,v|
    lbs = k.split("°")
    b[lbs[0]].push [ lbs[1], v] #Here I generate hash b
}

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

Я предлагаю вам сначала преобразовать значения одного из хэшей в хэши, как я объясню. Предположим, мы создаем новый b.

newbie = b.transform_values(&:to_h)
  #=> {"Us"=>{"1"=>["F", "O"], "2"=>["N"]},
  #    "Pa"=>{"1"=>["S", "D", "H"], "4"=>["K"]}}

Теперь мы можем использовать a и newbie для получения желаемого возвращаемого значения.

a.each_with_object({}) do |(k,v),h|
  h[k] = v.map do |first, arr|
    [first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
  end
end
  #=> {"Us"=>[["1", ["1|F", "2|O"]], ["2", ["1|N"]]],
  #    "Pa"=>[["1", ["1|S", "3|D", "5|H"]], ["4", ["7|K"]]]}

Если a можно изменить, это немного проще.

a.each do |k,v|
  v.map! do |first, arr|
    [first, arr.zip(newbie[k][first]).map { |pair| pair.join('|') }]
  end
end

Метод Hash # trasform_values ​​ дебютировал в Ruby v2.4. Для поддержки более старых версий нужно вычислить newbie следующим образом.

newbie = b.each_with_object({}) {|(k,v),h| h[k] = v.to_h }
0 голосов
/ 30 апреля 2018

В этом решении мы сохраним исходную структуру.

Я выполнил вашу первую попытку, но вместо:

a.merge(b) {|key, a_val, b_val| a_val.merge b_val }

Подумайте об использовании новой пользовательской функции слияния, такой как:

c = a.merge(b) {|key, a_val, b_val| myMergeArray(a_val, b_val) }

Тогда новая функция слияния является простой рекурсивной:

def myMergeArray(a,b,sep = '|')
 c = a
 c.each_with_index { |e, i|
    if c[i].is_a? Array 
        c[i] = myMergeArray(c[i], b[i], sep)
    else
        c[i] = c[i] + sep + b[i] if c[i] != b[i]
    end
        }
 return c
end

Я предположил, что в случае равных элементов просто сохраните один, например, «Y» и «Y» дают только «Y» вместо «Y | Y»

Ура!

0 голосов
/ 30 апреля 2018

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

Тем не менее, вы можете сделать это:

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

c = a.keys.map do |k|
  ah = a[k].to_h
  bh = b[k].to_h

  [
    k,
    ah.keys.map do |ka|
      [
        ka,
        ah[ka].zip(bh[ka]).map do |pair|
          pair.join('|')
        end
      ]
    end
  ]
end.to_h

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

Ключевым моментом здесь является строгое использование map для преобразования каждого слоя и zip для "объединения" двух массивов вместе в пары, которые затем можно объединить с join в нужную строковую цель. Примените к Hash с to_h в конце, и вы получите то, что хотите.

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

То, что вы хотите сделать, это обернуть это в метод с описательным именем:

def hash_compactor(a,b)
  # ... (code) ...
end

Это поможет сохранить его модульность. Обычно я пытаюсь создать решения, которые обрабатывают N аргументов, определяя его как:

def hash_compactor(*input)
  # ...
end

Где input - это массив различных наборов в заданной вами форме. Результирующий код на удивление намного сложнее.

Обратите внимание, что это делает много предположений о том, что входные данные идеально согласованы и взорвется, если это не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...