Группировать массив хэшей по ключу и добавлять вложенный массив к другому хеш-ключу - PullRequest
0 голосов
/ 27 ноября 2018

Мне нужно сократить массив хэшей в один хеш, если этот ключ хеша совпадает.

my_array - это массив хэшей, каждый из которых имеет два ключа: один объект активной записи,другой массив различных активных объектов записи, например:

my_array = [
  {
    first_hash_key: {id: 1, title: "my title"},
    second_hash_key: ["stuff", "more stuff", "more stuff"]
  },
  {
    first_hash_key: {id: 1, title: "my title"},
    second_hash_key: ["stuff 3", "uniq stuff 2"]
  },
  {
    first_hash_key: {id: 2, title: "my other title"},
    second_hash_key: ["interesting stuff", "uniq stuff"]
  }
]

Я хочу объединить хэши на first_hash_key[:id], добавить каждый элемент в hash_key_two в массив, а не перезаписать их, чтобы получить:

my_array = [
  {
    first_hash_key: {id: 1, title: "my title"},
    second_hash_key: [
      ["stuff", "more stuff", "more stuff"],
      ["stuff 3", "uniq stuff 2"]
    ]
  },
  {
    first_hash_key: {id: 2, title: "my other title"},
    second_hash_key: ["interesting stuff", "uniq stuff"]
  }
]

Я могу уменьшить массив и хэш верхнего уровня и использовать merge, но я теряю отдельные массивы внутри хеша.

Я также пытался группировать по id из first_hash_key, а затем вводить так:

my_array.group_by{|h| h[:first_hash_key]}.map{|k,v| v.inject(:merge)}

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

[
  {
    :first_hash_key=>{:id=>1, :title=>"my title"},
    :second_hash_key=>["stuff 3", "uniq stuff 2"]
  },
  {
    :first_hash_key=>{:id=>2, :title=>"my other title"}, 
    :second_hash_key=>["interesting stuff", "uniq stuff"]
  }
]

Обновление Как отметили sawa и cary, нет смысла угадывать ключ second_hash_key, если данные являются массивом или массивом или массивами, лучше всегда иметь массивмассивы.Желаемый вывод:

 [{:first_hash_key=>{:id=>1, :title=>"my title"}, 
   :second_hash_key=>[["stuff", "more stuff", "more stuff"], 
                      ["stuff 3", "uniq stuff 2"]]
 },
 {:first_hash_key=>{:id=>2, :title=>"my other title"}, 
 :second_hash_key=>[["interesting stuff", "uniq stuff"]]
 }]

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018
my_array.each_with_object({}) do |g,h|
  h.update(g[:first_hash_key][:id]=>g) { |_,o,n|
    o.merge(second_hash_key: [[*o[:second_hash_key]], n[:second_hash_key]]) }
end.values
  #=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
  #     :second_hash_key=>[["stuff", "more stuff", "more stuff"],
  #                        ["stuff 3", "uniq stuff 2"]]},
  #    {:first_hash_key=>{:id=>2, :title=>"my other title"},
  #     :second_hash_key=>["interesting stuff", "uniq stuff"]}]

Получатель значений Hash # - это хэш:

{1=>{:first_hash_key=>{:id=>1, :title=>"my title"},
     :second_hash_key=>[["stuff", "more stuff", "more stuff"],
                        ["stuff 3", "uniq stuff 2"]]},
 2=>{:first_hash_key=>{:id=>2, :title=>"my other title"},
     :second_hash_key=>["interesting stuff", "uniq stuff"]}}

Используется форма Hash # update (он же merge!).), который использует блок для определения значений ключей, которые присутствуют в обоих объединяемых хешах, в данном случае это ключи 1 и 2.Этот блок имеет три переменные блока: _ равно общему ключу 1 ;o («старый») - это значение ключа _ в создаваемом хеше, а n («новый») - это значение ключа _ в хеше, который объединяется в создаваемый хэш.

Выражение [*o[:second_hash_key]] преобразует o[:second_hash_key] в собственный массив, если o[:second_hash_key] не является массивом, и оставляет o[:second_hash_key] неизменным, если это уже массив.Например, [*1] #=> [1], тогда как [*[1,2]] #=> [1,2].

Возвращаемое значение проблематично в том смысле, что значение :second_hash_key в одном случае является массивом массивов, а в другом - просто массивом.Следовательно, в последующих вычислениях необходимо будет определить, является ли каждое значение массивом массивов или просто массивом, и затем принять соответствующие меры.Конечно, это можно сделать, но это грязно и безобразно;Лучше сделать все значения массивов массивов.Мы могли бы сделать это следующим образом.

my_array.each_with_object({}) do |g,h|
  h.update(g[:first_hash_key][:id]=>
    g.merge(second_hash_key: [g[:second_hash_key]])) { |_,o,n|
      o.merge(second_hash_key: o[:second_hash_key] + n[:second_hash_key]) }
end.values
  #=> [{:first_hash_key=>{:id=>1, :title=>"my title"},
  #     :second_hash_key=>[["stuff", "more stuff", "more stuff"],
  #                        ["stuff 3", "uniq stuff 2"]]},
  #    {:first_hash_key=>{:id=>2, :title=>"my other title"},
  #     :second_hash_key=>[["interesting stuff", "uniq stuff"]]}]

1 Использование подчеркивания - допустимой локальной переменной - для общего ключа должно информировать читателя, что он не используется в блокерасчет.

0 голосов
/ 27 ноября 2018

ну, это не красиво, но здесь вы идете:

my_array.
  group_by { |elem| elem[:first_hash_key] }.
  transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
  reduce([]) do |memo, (key, vals)|
    memo + [{first_hash_key: key, second_hash_key: vals}]
  end

возвращает:

[
  {
    :first_hash_key=>{:id=>1, :title=>"my title"},
    :second_hash_key=>[
      ["stuff", "more stuff", "more stuff"],
      ["stuff 3", "uniq stuff 2"]
    ]
  }, {
    :first_hash_key=>{:id=>2, :title=>"my other title"},
    :second_hash_key=>[
      ["interesting stuff", "uniq stuff"]
    ]
  }
]

Если вам не ясно, как это работает, вы должны проверить результат каждогострока, но для суммирования

  1. сгруппируйте по first_hash_key
  2. , вызовите transform_values, чтобы получить значения в second_hash_key, соответствующие каждому из этих first_hash_key значений

  3. В этот момент у вас есть хэш-отображение first_hash_key для всех соответствующих значений second_hash_key.Теперь нужно просто reduce получить окончательную структуру данных.

Этот шаблон group_by => transform_values ​​=> lower - это то, что я использую все время и очень полезно.Я считаю, что transform_values ​​доступен в rails, а также в ruby ​​2.5.

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