Как я могу использовать Array#delete при переборе массива?

У меня есть массив, который я хочу перебрать и удалить некоторые элементы. Это не работает:

a = [1, 2, 3, 4, 5]
a.each do |x|
  next if x < 3
  a.delete x
  # do something with x
end
a #=> [1, 2, 4]

Я хочу, чтобы a стал [1, 2]. Как я могу обойти это?


person Adrian    schedule 15.07.2010    source источник
comment
Возможный дубликат удаления во время итерации в Ruby?   -  person sschuberth    schedule 06.01.2016


Ответы (4)


a.delete_if { |x| x >= 3 }

См. документацию по методу здесь

Обновлять:

Вы можете обрабатывать x в блоке:

a.delete_if do |element|
  if element >= 3
    do_something_with(element)
    true # Make sure the if statement returns true, so it gets marked for deletion
  end
end
person Chubas    schedule 15.07.2010
comment
Мне нужно что-то сделать с x, если он будет удален. Я должен положить это в блок? - person Adrian; 16.07.2010
comment
@ Адриан Или вы можете использовать reject!. - person XåpplI'-I0llwlg'I -; 21.04.2014
comment
Следует иметь в виду, что reject! вернет nil, если ни один элемент не был отклонен, а delete_if вернет исходный массив. - person Jan Klimo; 16.09.2015

Вам не нужно удалять из массива, вы можете отфильтровать его так:

a = [1, 2, 3, 4, 5]

b = a.select {|x| x < 3}

puts b.inspect # => [1,2]

b.each {|i| puts i} # do something to each here
person Joc    schedule 15.07.2010
comment
Извините, что поднимаю пост о зомби, но любопытно, увеличивает ли подмножество подхода b = a использование памяти или использует ли он внутренние указатели или что-то еще для ссылки на исходные элементы a? Причина, по которой я спрашиваю, заключается в том, что мой вариант использования довольно чувствителен к памяти, поэтому любопытно, является ли удаление из исходного массива или выбор лучшим методом... - person gorlaz; 21.11.2015
comment
@gorlaz .select возвращает новый рубиновый объект, который будет использовать больше памяти. См. этот пример pastebin.com/23e10bKy - person lacostenycoder; 06.12.2019
comment
Это также более функционально в том смысле, что данные не изменяются. - person pedz; 04.05.2020

Я задавался этим вопросом не так давно.

Удаление во время итерации в Ruby?

Это не работает, потому что Ruby выходит из цикла .each при попытке что-то удалить. Если вы просто хотите удалить что-то из массива, delete_if будет работать, но если вам нужен больший контроль, решение, которое у меня есть в этом потоке, работает, хотя оно довольно уродливое.

person Jesse Jashinsky    schedule 15.07.2010
comment
Удаление не прерывает цикл. each эффективно выполняет итерацию с использованием индекса. Поэтому, если вы удалите, индексы будут другими, и следующая итерация будет использовать новые индексы. Попробуйте это: arr = [1,2,3,4,5,6]; arr.each_with_index {|e,i| p [arr, e, i]; next if e < 3; arr.delete e }. Когда достигается элемент 3, индекс равен 2. После удаления 3 следующая итерация увеличивает индекс до 3, а элемент равен 5. Таким образом, 4 пропускается. 6 пропущен по той же причине. - person Kelvin; 04.01.2013

Другой способ сделать это - использовать reject!, что, возможно, понятнее, поскольку у него есть !, что означает «это изменит массив». Единственное отличие состоит в том, что reject! вернет nil, если не было сделано никаких изменений.

a.delete_if {|x| x >= 3 }

or

a.reject! {|x| x >= 3 }

оба будут работать нормально.

person AlexChaffee    schedule 06.03.2014