Как соединить два массива на основе общего набора ключей в Ruby - PullRequest
0 голосов
/ 22 марта 2020

У меня есть два массива хэшей, связанных общим набором ключей:

Массив 1:

[
  {0=>"pmet-add-install-module-timings.patch"},
  {1=>"pmet-change-sample-data-load-order.patch"},
  {2=>"pmet-configurable-recurring.patch"},
  {3=>"pmet-consumers-run-staggered-by-sleep.patch"},
  {4=>"pmet-dynamic-block-segment-display.patch"},
  {5=>"pmet-fix-admin-label-word-breaking.patch"},
  {6=>"pmet-fix-invalid-module-dependencies.patch"},
  {7=>"pmet-fix-invalid-sample-data-module-dependencies.patch"},
  {8=>"pmet-fix-module-loader-algorithm.patch"},
  {9=>"pmet-fix-sample-data-code-generator.patch"},
  {10=>"pmet-remove-id-requirement-from-layout-update-file.patch"},
  {11=>"pmet-specify-store-id-for-order.patch"},
  {12=>"pmet-staging-preview-js-fix.patch"},
  {13=>"pmet-stop-catching-sample-data-errrors-during-install.patch"},
  {14=>"pmet-visitor-segment.patch"}
]

Массив 2:

[
  {0=>"magento2-base"},
  {1=>"magento/module-sample-data"},
  {2=>"magento/module-configurable-sample-data"},
  {3=>"magento/module-message-queue"},
  {4=>"magento/module-banner"},
  {5=>"magento/theme-adminhtml-backend"},
  {6=>"magento/module-staging"},
  {7=>"magento/module-gift-registry-sample-data"},
  {8=>"magento2-base"},
  {9=>"magento/module-downloadable-sample-data"},
  {10=>"magento/module-catalog"},
  {11=>"magento/module-sales-sample-data"},
  {12=>"magento/module-staging"},
  {13=>"magento2-base"},
  {14=>"magento/module-customer"}
]

Хеши в этих массивах имеют одинаковый набор индексов, а второй массив имеет повторяющиеся значения в ключах 0, 8 и 13, а также в 6 и 12.

Моя цель - объединить значения из этих двух наборов данных в набор вложенных хэшей. Везде, где есть дублированное значение в массиве 2, мне нужно собрать связанные с ним значения из массива 1 и включить их во вложенный ха sh.

Например, взять значения magento2-base из массива 2 и связанные с ключом значения из массива 1. Структура ha sh в Ruby будет выглядеть следующим образом:

hash = {
  "magento2-base" => [
    {0 => "m2-hotfixes/pmet-add-install-module-timings.patch"},
    {8 => "m2-hotfixes/pmet-fix-module-loader-algorithm.patch"},
    {13 => "m2-hotfixes/pmet-stop-catching-sample-data-errrors-during-install.patch"}
  ]
}

То же самое будет справедливо для любых других дублированных значений из массива 2, например, magento/module-staging будет:

hash = {
  "magento/module-staging" => [
    {6 => "pmet-fix-invalid-module-dependencies.patch"},
    {12 => "pmet-staging-preview-js-fix.patch"}
  ]
}

Большая выдержка из результирующего га sh, которая объединяет эти потребности вместе будет выглядеть следующим образом:

hash = {
  "magento2-base" => 
  [
    {0 => "m2-hotfixes/pmet-add-install-module-timings.patch"},
    {8 => "m2-hotfixes/pmet-fix-module-loader-algorithm.patch"},
    {13 => "m2-hotfixes/pmet-stop-catching-sample-data-errrors-during-install.patch"}
  ],
  "magento/module-sample-data" => 
  {0 => "pmet-change-sample-data-load-order.patch"},
    "magento/module-configurable-sample-data" =>
  {2 => "pmet-configurable-recurring.patch"},
    "magento/module-message-queue" =>
  {3 => "pmet-consumers-run-staggered-by-sleep.patch"}
  "magento/module-staging" => 
  [
    {6 => "pmet-fix-invalid-module-dependencies.patch"},
    {12 => "pmet-staging-preview-js-fix.patch"}
  ],
    ...
}

Я использовал вложенный l oop, который объединяет оба массива для соединения ключей, и попытался извлечь дубликаты из массива 2, и думал, что мне нужно чтобы поддерживать как массив дублированных значений из массива 2, так и массив связанных с ними значений из массива 1. Затем я бы использовал некоторый массив слияния magi c, чтобы собрать все вместе.

Вот что у меня есть:

found_modules_array = []
duplicate_modules_array = []
duplicate_module_hash = {}
file_collection_array = []

modules_array.each do |module_hash|
  module_hash.each do |module_hash_key, module_hash_value|
    files_array.each do |file_hash|
      file_hash.each do |file_hash_key, file_hash_value|
        if module_hash_key == file_hash_key
          if found_modules_array.include?(module_hash_value)
            duplicate_module_hash = {
              module_hash_key => module_hash_value
            }
            duplicate_modules_array << duplicate_module_hash
          end
          found_modules_array << module_hash_value
        end
      end
    end
  end
end

В этом коде files_array - это Массив 1, а modules_array - Массив 2. found_modules_array - это контейнер для хранения любых дубликатов перед нажатием их в duplicate_module_hash, который затем будет вытолкнут в duplicates_modules_array.

Это решение:

  1. Не работает
  2. Не использует преимущество мощности Ruby
  3. Не работает

РЕДАКТИРОВАТЬ

Путь к вышеупомянутой структуре данных подробно объясняется в следующий пост: Использование значений массива в качестве га sh ключей для создания вложенных хэшей в Ruby

Я подведу итоги ниже:

У меня есть каталог файлы. Большинство из них .patch файлов, хотя некоторые из них нет. Для каждого файла патча мне нужно отсканировать первую строку, которая всегда является строкой, и извлечь часть этой строки. С комбинацией имени каждого файла, этой части каждой первой строки и уникального идентификатора для каждого файла мне нужно создать ха sh, который я затем преобразую в json и запишу в файл.

Вот примеры:

Каталог файлов:

|__ .gitkeep
|__ pmet-add-install-module-timings.patch
|__ pmet-change-sample-data-load-order.patch

Примеры первой строки:

File Name: `pmet-add-install-module-timings.patch`
First Line: `diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php`

File Name: `pmet-change-sample-data-load-order.patch`
First Line: `diff --git a/vendor/magento/module-sample-data/etc/module.xml b/vendor/magento/module-sample-data/etc/module.xml`

File Name: `pmet-stop-catching-sample-data-errrors-during-install.patch`
First Line: `diff --git a/vendor/magento/framework/Setup/SampleData/Executor.php b/vendor/magento/framework/Setup/SampleData/Executor.php`

File Name: `pmet-fix-admin-label-word-breaking.patch`
First Line: `diff --git a/vendor/magento/theme-adminhtml-backend/web/css/styles-old.less b/vendor/magento/theme-adminhtml-backend/web/css/styles-old.less`

Пример Json Файл:

{
    "patches": {
        "magento/magento2-base": {
            "Patch 1": "m2-hotfixes/pmet-add-install-module-timings.patch"
        },
        "magento/module-sample-data": {
            "Patch 2": "m2-hotfixes/pmet-change-sample-data-load-order.patch"
        },
        "magento/theme-adminhtml-backend": {
            "Patch 3": "m2-hotfixes/pmet-fix-admin-label-word-breaking.patch"
        },
        "magento/framework": {
            "Patch 4": "m2-hotfixes/pmet-stop-catching-sample-data-errrors-during-install.patch"
        }
    }
}

Проблема, с которой я столкнулся, заключается в том, что хотя json допускает дублирование ключей, ruby хэши не позволяют, поэтому элементы были удалены из файла json, поскольку они были удалены из ha sh , Чтобы решить эту проблему, я предположил, что мне нужно создать структуру массива, которую я указал, чтобы сохранить идентификаторы в качестве непротиворечивого идентификатора между очищенными файлами и соответствующими данными, принадлежащими им, чтобы я мог собрать данные вместе в другом порядке. Теперь я понимаю, что это не так, поэтому я переключил подход на использование следующего:

files.each_with_index do |file, key|
    value = File.open(file, &:readline).split('/')[3]
    if value.match(/module-/) || value.match(/theme-/)
        result = "magento/#{value}"
    else
        result = "magento2-base"
    end
    file_array << file
    module_array << result
end

Это дает плоские хеши, которые были предложены ниже.


Ответы [ 2 ]

2 голосов
/ 22 марта 2020

Итак, во-первых, структура

arr1 = [
    {0=>"pmet-add-install-module-timings.patch"},
    {1=>"pmet-change-sample-data-load-order.patch"},
    {2=>"pmet-configurable-recurring.patch"},
    {3=>"pmet-consumers-run-staggered-by-sleep.patch"},
    # etc
]

немного странная. С ним проще работать, как с плоской ха sh, например,

h1 = {
    0 => "pmet-add-install-module-timings.patch",
    1 => "pmet-change-sample-data-load-order.patch",
    2 => "pmet-configurable-recurring.patch",
    3 =>"pmet-consumers-run-staggered-by-sleep.patch",
    # etc
}

К счастью, между ними довольно легко трансформироваться:

h1 = arr1.reduce(&:merge)
h2 = arr2.reduce(&:merge)

С этого момента, Enumerable методы (в этом случае вечно полезная map , group_by и transform_values ​​) проведут вас до конца:

indexed_by_val = h2.
  group_by { |k,v| v }.
  transform_values { |vals| vals.map(&:first) }

, которая дает вам карту val для индексов:

{
  "magento2-base"=>[0, 8, 13],
  "magento/module-sample-data"=>[1],
  "magento/module-configurable-sample-data"=>[2],
  # etc
}

, а затем мы можем заменить эти списки индексов соответствующими значениями в h1:

result = indexed_by_val.transform_values do |indexes|
  indexes.map do |idx|
    { idx => h1[idx] }
  end
end

которая создает желаемую структуру данных:

{
  "magento2-base"=>[
    {0=>"pmet-add-install-module-timings.patch"},
    {8=>"pmet-fix-module-loader-algorithm.patch"},
    {13=>"pmet-stop-catching-sample-data-errrors-during-install.patch"}
  ],
  "magento/module-sample-data"=>[
    {1=>"pmet-change-sample-data-load-order.patch"}
  ],
  "magento/module-configurable-sample-data"=>[
    {2=>"pmet-configurable-recurring.patch"}
  ],
  # etc
}

Я заметил, что в ожидаемом вами выводе, который вы указали, значения являются хешами или массивами. Я бы рекомендовал против этой практики. Гораздо лучше иметь единый тип данных для всех ключей и значений ha sh. Но, если вы действительно хотите сделать это по какой-либо причине, это не так уж сложно:

# I am not advising this approach
result2 = result.transform_values do |arr|
  arr.length > 1 ? arr : arr[0]
end

Кстати, я знаю, что этот вид функционального программирования / перечислимого кода цепочки может быть немного трудно расшифровать , поэтому я рекомендую запускать его построчно для вашего понимания.


Предполагая, что вы используете унифицированную структуру данных, о которой я упоминал выше, я бы рекомендовал вызывать .transform_values { |vals| vals.reduce(&:merge) } для вашего окончательного результата, чтобы значения - это отдельные хеши вместо нескольких хешей:

{
  "magento2-base"=>{
    0=>"pmet-add-install-module-timings.patch",
    8=>"pmet-fix-module-loader-algorithm.patch",
    13=>"pmet-stop-catching-sample-data-errrors-during-install.patch"
  },
  "magento/module-sample-data"=>{
    1=>"pmet-change-sample-data-load-order.patch"
  ],
  "magento/module-configurable-sample-data"=>{
    2=>"pmet-configurable-recurring.patch"
  },
  # etc
}
1 голос
/ 22 марта 2020

Пусть arr1 и arr2 будут вашими двумя массивами. Из-за того, что они имеют одинаковый размер и что для каждого индекса i, arr1[i][i] и arr2[i][i] являются значениями ключа i хешей arr1[i] и arr2[i], желаемый результат может получить довольно легко:

arr2.each_with_index.with_object({}) do |(g,i),h|
  (h[g[i]] ||= []) << arr1[i][i]
end
  #=> {
  #    "magento2-base"=>[
  #    "pmet-add-install-module-timings.patch",
  #    "pmet-fix-module-loader-algorithm.patch",
  #    "pmet-stop-catching-sample-data-errrors-during-install.patch"
  #    ],
  #    "magento/module-sample-data"=>[
  #      "pmet-change-sample-data-load-order.patch"
  #    ],
  #    ...
  #    "magento/module-staging"=>[
  #      "pmet-fix-invalid-module-dependencies.patch",
  #      "pmet-staging-preview-js-fix.patch"
  #    ],
  #    "magento/module-customer"=>[
  #      "pmet-visitor-segment.patch"
  #    ]
  #   } 

Фрагмент

h[g[i]] ||= []

эффективно расширяется до

h[g[i]] = h[g[i]] || []  # *

Если га sh h не имеет ключа [g[i]],

h[g[i]] #=> nil

, поэтому * становится

h[g[i]] = nil || [] #=> []

, после чего

h[g[i]] << "cat"
  #=> ["cat"]

(что также работает с "dog"). Вместо этого вышеприведенное выражение может быть записано:

arr2.each_with_index.with_object(Hash.new {|h,k| h[k]=[]}) do |(g,i),h|
  h[g[i]] << arr1[i][i]
end

Здесь используется форма Ha sh :: new , в которой используется блок (здесь {|h,k| h[k]=[]}), который вызывается, когда ha sh доступен по значению, которое не является одним из его ключей.

Альтернативный метод:

arr2.each_with_index.with_object({}) do |(g,i),h|
  h.update(g[i]=>[arr1[i][i]]) { |_,o,n| o+n }
end

Используется форма Hash # update (он же merge!), который использует блок для определения значений ключей, которые находятся в обоих объединяемых хэшах.

Третий способ - использовать Enumerable # group_by :

arr2.each_with_index.group_by { |h,i| arr2[i][i] }.
     transform_values { |a| a.map { |_,i| arr1[i][i] } }
...