Avatar ·

Как в Swift перебрать массив с индексом и элементом (аналог enumerated)

Привет! Я только начал изучать Swift и столкнулся с типичной задачей: нужно пройтись по массиву и на каждой итерации получить не только сам элемент, но и его индекс (позицию в массиве). В других языках я привык к классическому циклу for с счётчиком, например, for i in 0..>>

Основное решение: метод enumerated()

В Swift для итерации по массиву с одновременным получением индекса и элемента используется метод enumerated(), который возвращает последовательность кортежей (offset: Int, element: Element). Вот как это работает:

let fruits = ["Apple", "Banana", "Cherry"]
for (index, fruit) in fruits.enumerated() {
    print("Index \(index): \(fruit)")
}
// Вывод:
// Index 0: Apple
// Index 1: Banana
// Index 2: Cherry

Обратите внимание: enumerated() возвращает смещение (offset), которое в 99% случаев совпадает с индексом, но технически это не одно и то же. Для массивов разницы нет, но для других коллекций (например, словарей) смещение может не соответствовать ключу.

Изменение элементов массива во время итерации

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

var numbers = [10, 20, 30, 40]
for (index, value) in numbers.enumerated() {
    numbers[index] = value * 2
}
print(numbers) // [20, 40, 60, 80]

Однако более идиоматичный способ для модификации — использовать метод indices:

for index in numbers.indices {
    numbers[index] *= 2
}

Работа с многомерными массивами

Для двумерных массивов вложите циклы с enumerated():

let matrix = [[1, 2], [3, 4], [5, 6]]
for (rowIndex, row) in matrix.enumerated() {
    for (colIndex, value) in row.enumerated() {
        print("matrix[\(rowIndex)][\(colIndex)] = \(value)")
    }
}

Важные нюансы

  • Не путайте с zip(): zip(collection, collection.indices) делает похожее, но менее удобно.
  • Производительность: enumerated() не создаёт новый массив — это ленивая последовательность, поэтому overhead минимален.
  • Альтернатива для обратной итерации: используйте reversed().enumerated(), но помните, что индексы при этом сохраняют исходный порядок. Если нужно получить индексы в обратном порядке, лучше использовать indices.reversed().
  • Для словарей: enumerated() даст вам смещение по порядку пар ключ-значение, но не ключ. Для итерации по ключам и значениям используйте for (key, value) in dictionary.

Полный пример с дополнительными возможностями

let names = ["Alice", "Bob", "Charlie", "Diana"]

// Только чётные индексы
for (index, name) in names.enumerated() where index.isMultiple(of: 2) {
    print("\(name) at even index \(index)")
}

// С преобразованием в массив кортежей
let indexedNames = names.enumerated().map { (index, name) in
    return "\(index + 1). \(name)"
}
print(indexedNames) // ["1. Alice", "2. Bob", "3. Charlie", "4. Diana"]

// Фильтрация по элементу
for (index, name) in names.enumerated() where name.hasPrefix("C") {
    print("Found \(name) at index \(index)")
}

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

<<>>

Альтернативный подход: классический цикл с indices

Если вам не нравится синтаксис кортежей или нужно больше контроля над индексом, используйте свойство indices массива. Оно возвращает диапазон допустимых индексов (тип Range<Int>):

let fruits = ["Apple", "Banana", "Cherry"]
for index in fruits.indices {
    print("Index \(index): \(fruits[index])")
}

Преимущества:

  • Чётко отделяет индекс от элемента — вы явно обращаетесь к массиву по индексу.
  • Идеально подходит для модификации массива, так как вы сразу работаете с индексом.
  • Не создаёт лишних кортежей, что может быть чуть быстрее в горячих циклах (хотя разница обычно незначительна).

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

Когда использовать какой подход?

  • enumerated() — когда нужно просто прочитать массив и получить индекс рядом с элементом. Это более «декларативный» стиль.
  • indices — когда вы планируете изменять массив или когда логика цикла сложнее (например, пропуск индексов, обратная итерация).

Оба способа корректны, выбирайте тот, который делает ваш код понятнее в конкретной ситуации.