Avatar ·

Как в Swift перебрать массив с получением индекса и элемента одновременно?

Всем привет! Я только начал изучать Swift и столкнулся с такой проблемой. Мне нужно перебрать массив строк, но при этом на каждой итерации мне нужен не только сам элемент, но и его индекс (позиция) в массиве. Вот пример того, что я пытаюсь сделать. У меня есть массив имён: ```swift let names = ["Анна", "Борис", "Виктор", "Галина"] ``` Я хочу вывести на экран что-то вроде: "1. Анна" "2. Борис" и так далее. Пока я делаю так: ```swift for i in 0..>>

Использование метода enumerated() — правильный и безопасный способ

В Swift для перебора массива с одновременным получением индекса и элемента существует встроенный метод enumerated(). Он возвращает последовательность кортежей (tuple), где каждый кортеж состоит из индекса (начиная с 0) и соответствующего элемента массива.

Вот как решить вашу задачу:

let names = ["Анна", "Борис", "Виктор", "Галина"]

for (index, name) in names.enumerated() {
    print("\(index + 1). \(name)")
}

Этот код выведет:

1. Анна
2. Борис
3. Виктор
4. Галина

Как это работает

  • names.enumerated() создаёт ленивую последовательность, которая на каждой итерации выдаёт кортеж (Int, Element).
  • Мы используем связывание кортежа (tuple pattern binding) — for (index, name) in ..., чтобы сразу получить индекс и элемент.
  • Индекс начинается с 0. Если вам нужен отсчёт с 1, просто прибавьте 1: index + 1.

Почему это лучше, чем for i in 0..<count

  • Безопасность: Вы не рискуете выйти за границы массива. enumerated() гарантированно перебирает ровно столько элементов, сколько есть в массиве.
  • Читаемость: Код становится самодокументируемым — сразу видно, что вы работаете и с индексом, и с элементом.
  • Производительность: enumerated() не создаёт копию массива. Это ленивая операция — элементы извлекаются по мере необходимости.
  • Универсальность: Работает с любыми коллекциями, а не только с массивами: Set, Dictionary, String (как коллекция символов) и т.д.

Более сложный пример с фильтрацией

Предположим, вам нужно получить индексы только чётных чисел из массива:

let numbers = [10, 15, 20, 25, 30, 35]
let evenIndexes = numbers.enumerated().compactMap { (index, value) -> Int? in
    return value.isMultiple(of: 2) ? index : nil
}
print(evenIndexes) // [0, 2, 4]

Важный нюанс: изменение массива во время итерации

Если вы попытаетесь изменить массив (добавить или удалить элементы) внутри цикла for (index, name) in names.enumerated(), компилятор выдаст ошибку. Это защита от неопределённого поведения. Если вам нужно модифицировать коллекцию во время итерации, используйте for index in names.indices.reversed() или работайте с копией.

Альтернатива: метод indices

Если вам нужны именно индексы, а не элементы, можно использовать indices:

for index in names.indices {
    print("\(index): \(names[index])")
}

Но это менее удобно, чем enumerated(), так как требует повторного обращения к массиву.

В итоге: всегда используйте enumerated(), когда вам нужен индекс вместе с элементом. Это идиоматичный, безопасный и производительный способ в Swift.

<<>>

Использование zip и indices для нестандартных случаев

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

Пример с тем же массивом имён:

let names = ["Анна", "Борис", "Виктор", "Галина"]

for (index, name) in zip(names.indices, names) {
    print("\(index + 1). \(name)")
}

Это даст тот же результат. zip создаёт последовательность пар из двух последовательностей. Здесь мы берём индексы массива (names.indices) и сами элементы (names).

Когда это может быть полезно

  • Нестандартный шаг: Если вам нужны только чётные индексы, вы можете отфильтровать names.indices перед zip.
  • Два массива: zip отлично подходит для параллельного перебора двух коллекций: for (name, age) in zip(names, ages).
  • Контроль над индексами: Вы можете использовать names.indices.dropFirst(), чтобы пропустить первый элемент, и т.д.

Однако для большинства повседневных задач enumerated() остаётся предпочтительным вариантом из-за своей простоты и читаемости. zip стоит применять только тогда, когда enumerated() не хватает гибкости.