Ruby .each с удалением предметов в коллекции - PullRequest
0 голосов
/ 16 февраля 2010

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

SAFE = ;
return control if control.CyclesCount == 0;
control.Items.each{|item| control.Items.Remove(item) if item.Value.index('|').nil?};
return control;
Элемент управления

- это пользовательский элемент управления, а его элементы - ListItemCollction. Я выполняю модульные тесты, чтобы исправить мой код Ruby, и у меня возникают проблемы. Элемент ListItemColletion, который я передаю, выглядит следующим образом ..

var lic = new ListItemCOllection {
  new ListItem {Text = "Item2", Value = "8"}, 
  new ListItem {Text = "Item1", Value = "1"},
  new ListItem {Text = "Item3", Value = "3|1"},
  new ListItem {Text = "Item4", Value = "4"},
  new ListItem {Text = "Item5", Value = "5|2"},
  new ListItem {Text = "Item6", Value = "6"}
}

Вместо того, чтобы оставлять 2 элемента с трубкой в ​​них, этот код всегда оставляет 3 элемента в коллекции элементов. Эти 3 зависят от порядка, в котором я помещаю предметы (в то время как в этом порядке, Item1, Item3, Item5 остаются слева), что приводит меня к мысли, что это удаление, которое испортилось Я также попытался взять копию коллекции, перебрать ее, удалив из оригинала, чтобы не удалять из коллекции, через которую я перебирал. Я новичок по отношению к Руби, так что будь осторожен со мной ... но я мог бы воспользоваться некоторым советом.

Спасибо

Ответы [ 3 ]

5 голосов
/ 16 февраля 2010

Не рекомендуется изменять массив во время итерации по нему. Есть несколько итераторов, целью которых является изменение массива.

a= [1,2,3]
b= [1,2,3]
a.delete_if { |x| x == 2 } # => [1,3]
b.reject! { |x| x == 2 } # => [1,3]
a # => [1,3]
b # => [1,3]

Array#delete_if удаляет элементы массива. Существует только незначительная разница с Array#reject

a= [1,2,3]
b= [1,2,3]
a.delete_if { |x| false } # => [1,3]
b.reject! { |x| false } # => nil
a # => [1,2,3]
b # => [1,2,3]

Array#delete_if всегда возвращает оставшийся массив. Array#reject! возвращает ноль вместо этого, если массив остается неизменным.

Еще несколько модификаторов, которые не меняют исходный массив:

a= [1,2,3]
a.reject { |x| x == 2 } # => [1,3]
a # => [1,2,3]

Array#reject возвращает массив без отклоненных элементов, но не изменяет исходный массив.

a= [1,2,3]
a.select { |x| x != 2 } # => [1,2,3]
a # => [1,3]

Array#select возвращает массив только выбранных элементов, но не изменяет исходный массив.

3 голосов
/ 16 февраля 2010

Изменение коллекции , пока вы выполняете ее итерацию, - никогда хорошая идея. Если вы сделаете это, весь ад вырвется на свободу. (Предпочтительно, это вызовет какое-то исключение, но это жизнь ...)

Однако это не самая большая проблема в вашем коде. Самая большая проблема в том, что вы совершили смертный грех Руби: не зная Enumerable. (Не волнуйтесь: каждый совершает этот грех. Все время.)

Если вы хотите отклонить все элементы из коллекции, которые удовлетворяют условию, есть метод для этого, и он называется именно так, как вы ожидаете: Enumerable#reject!.

Итак, давайте очистим это, не так ли?

SAFE = ;

Что там делает точка с запятой? Похоже, вы перепутали C # и Ruby :-) (О, а также, эта строка не делает ничего полезного, не так ли?)

return control if control.CyclesCount == 0;

Опять бесполезная точка с запятой.

control.Items.each{|item| control.Items.Remove(item) if item.Value.index('|').nil?};

Вот где это становится интересным:

control.Items.reject! {|item| item.Value.include?('|') }

Намного лучше, не правда ли?

return control;

Мне лично нравится зарезервировать ключевое слово return для «чистых» методов (т. Е. Методов, которые не имеют побочных эффектов), поэтому я бы не стал использовать его здесь, поскольку код изменяет control.Items, но выбор стиля. Сложив все это вместе, я бы написал так:

return control if control.cycles_count == 0
control.items.reject! {|item| item.value.include?('|') }
control

Примечание: у меня сейчас нет работающей установки IronRuby, поэтому я делаю несколько предположений, которые, к сожалению, не могу проверить:

  • работает метод транслитерации имени (CyclesCount -> cycles_count),
  • Value - это какая-то String или коллекция и
  • ListItemCollection миксы в Enumerable

Последний должен иметь место, если ListItemCollection реализует IEnumerable (в противном случае я бы посчитал это ошибкой в ​​IronRuby). Если ListItemCollection не не реализует IEnumerable (что я, вероятно, считаю ошибкой в ​​ListItemCollection), это все равно легко исправить:

class ListItemCollection; include Enumerable end

[Кстати: я бы также ввел метод cycles? (или свойство bool HasCycles на стороне .NET), чтобы вы могли избавиться от теста cycle_count == 0.]

1 голос
/ 16 февраля 2010

Если вы просто хотите удалить элементы из массива на основе условия, вы должны использовать Array#reject!:

control.Items.reject! {|item| item.Value.index('|').nil? };

Однако для правильной отладки нам нужно знать, как выглядит control.Items на Ruby-конце.

...