Как написать цикл for в Swift 3 для массива, который я изменяю во время цикла for?

Итак, у меня есть цикл for, который выглядит примерно так:

for var i = 0; i < results.count ; i += 1 {
   if (results[i] < 5) {
      results.removeAtIndex(i)
      i -= 1
   }
}

Раньше это работало. Но когда я изменил его на предпочитаемый синтаксис Swift 3.0:

for var i in 0..<results.count {
   if (results[i] < 5) {
      results.removeAtIndex(i)
      i -= 1
   }
}

Я получаю исключение массива IOOBE, потому что он не перепроверяет счетчик и продолжается до исходного results.count.

Как это исправить? Сейчас это работает, но я не хочу попасть в беду в будущем.


person QuantumHoneybees    schedule 02.06.2016    source источник
comment
этот код не жалуется на var i в 0..‹results.count { if (results[i] ‹ 5) { results.removeAtIndex(i) i -= 1 } }   -  person Amr Angry    schedule 19.02.2017
comment
не имеет смысла, почему Apple сделала цикл for таким сложным, я никогда не помню, чтобы у меня были проблемы с ним за последние 25 лет.   -  person Klajd Deda    schedule 20.10.2017


Ответы (4)


Хотя решение, использующее filter, является прекрасным решением и более Swift-ly, есть и другой способ, если использование for-in все же желательно:

var results = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for var i in (0..<results.count).reverse()
{
    if (results[i] < 5)
    {
        results.removeAtIndex(i)
        //i -= 1
    }
}

print(results)

Результат:

[5, 6, 7, 8, 9, 10]

Кроме того, мы могли бы вообще опустить эту строку i -= 1.

Проблема с removeAtIndex в цикле заключается в том, что он не приведет к переиндексации массива на месте и, таким образом, вызовет исключение массива за пределами границ из-за того, что count не обновляется.

Таким образом, перемещаясь назад, можно избежать исключения за пределами границ.

person Unheilig    schedule 03.06.2016
comment
О боже... это действительно умно. Да, я знаю, что способ фильтрации был бы более быстрым, но я думаю, что этот способ лучше подходит, я его использую. Благодарность! - person QuantumHoneybees; 04.06.2016
comment
ура, редактор ОП, который на самом деле дает хорошо принятый ответ, а не просто редактирует для увеличения репутации. редкий. - person aremvee; 29.03.2017
comment
@Unheiling - .reverse() эта функция не поддерживается в swift3. Кто-нибудь может больше помочь по тому же вопросу? - person New-Learner; 31.01.2018
comment
Вы должны удалить ключевое слово var в for var i in (0..<results.count).reverse() - person atulkhatri; 13.03.2018

Не могли бы вы использовать filter вместо этого?

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let greaterThan5 = numbers.filter{$0 >= 5}
print(greaterThan5)
person Brandon Shega    schedule 02.06.2016
comment
Новый массив должен называться greaterThan4 ;) В любом случае это правильный способ решения проблемы. - person Luca Angeletti; 02.06.2016
comment
Ах да правильно! функция, которая вызывает это, называется filterArray. Я так и не установил связь, ха-ха. Но мне также нужно, чтобы отфильтрованные результаты были добавлены в конец. Какие-либо предложения? - person QuantumHoneybees; 03.06.2016

Если вы хотите продолжить использовать цикл for, вы можете перечислить как индекс, так и элемент, используя enumerate:

for (index, element) in results.enumerate() {
   if (element < 5) {
     results.removeAtIndex(index)
   }
}

Хотя в зависимости от того, что вы делаете в своем цикле, метод filter может быть лучшей идеей.

person Luka Jacobowitz    schedule 02.06.2016
comment
Это плохая идея, вы столкнетесь с ловушкой вне диапазона при определенных обстоятельствах, например, попробуйте массив [6, 5, 4, 3, 2, 1]. Причина в том, что массив изменяется в цикле, но индекс не корректируется. - person vadian; 02.06.2016

Если ваша петля идет вперед...

for var i in (0..<results.count) where results.indices.contains(i) { 

//if the index doesn't exist, the loop will be stopped.

if (results[i] < 5) {
        results.removeAtIndex(i) 
    }

}
person Miguel Herrero    schedule 18.08.2017