Массив изменяется во время перечисления - PullRequest
0 голосов
/ 01 июля 2019

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

У меня есть массив хэшей с такими элементами, как my_hsh = {"numbers" => [1, 2, 3, 4], "n_count" => 4}

Что мне нужно сделать:

  1. Перебрать родительский массив и выбрать подходящие хэши,
  2. добавить каждый такой хэш в массив my_arr_nochange,
  3. перебирает "числа" в каждом таком хэше и добавляет их к the_hash["numbers"],
  4. добавить хэши без этих чисел в my_arr_updt.

Итак, код:

the_hash = {"numbers" => []}
my_arr_updt = []
my_arr_nochange = []
array_of_hashes.each do |my_hsh|
  if my_hsh["n_count"] == 4
    my_arr_nochange << my_hsh
    updated_hsh = my_hsh
    my_hsh["numbers"].each do |num|
      if num == 2
        the_hash["numbers"] += [ num ]
        updated_hsh["numbers"] -= [ num ]
      end
    end
    my_arr_updt << updated_hsh
  end
end

return the_hash, my_arr_updt, my_arr_nochange

Проблема в том, что my_arr_nochange изменяется, поэтому вместо получения старого состояния my_hsh внутри я получаю новое. Как:

my_arr_updt
=> [{"numbers" => [1, 3, 4], "n_count" => 4}]
my_arr_nochange 
=> [{"numbers" => [1, 3, 4], "n_count" => 4}]

Пробное расщепление различными методами и с использованием под-переменных. Нет результата.

P.S .: Если бы вы могли помочь с более подходящим названием, я был бы также признателен.

1 Ответ

6 голосов
/ 01 июля 2019

Я считаю, что ваша проблема updated_hsh = my_hsh.

Это не дублирует хеш. Любые изменения в updated_hsh изменят my_hsh и наоборот.

Использование Object # clone или Object # dup - шаг в правильном направлении, но фактически он не будет дублировать внутренние объекты (массивы "numbers"):

h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map(&:clone)
h2[0][:numbers] << 4
h2[0][:n_count] += 1

h1
# => [{numbers: [1,2,3,4], n_count: 3}]

Вы можете видеть, что n_count не был изменен в оригинале, но numbers был.

Чтобы обойти это, вы можете использовать Hash # deep_dup . Этот метод недоступен в ядре Ruby. Это часть Active Support, которая требуется Rails и может быть легко загружена в обычную Ruby-программу, если вам нужен гем.

require 'active_support/all'

h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map(&:deep_dup)
h2[0][:numbers] << 4
h2[0][:n_count] += 1

h1
# => [{numbers: [1,2,3], n_count: 3}]

Вы также можете реализовать deep_dup самостоятельно, если не хотите использовать активную поддержку. См. Как создать глубокую копию объекта в Ruby?

Другой альтернативой является ручное построение внутренних хэшей, например:

h1 = [{numbers: [1,2,3], n_count: 3}]
h2 = h1.map do |hsh|
  {
    numbers: hsh[:numbers].clone,
    n_count: hsh[:n_count]
  }
end

Хотя deep_dup более лаконичен

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