Как в Swift перебрать массив с индексом и элементом (аналог enumerated)
Основное решение: метод 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 — когда вы планируете изменять массив или когда логика цикла сложнее (например, пропуск индексов, обратная итерация).
Оба способа корректны, выбирайте тот, который делает ваш код понятнее в конкретной ситуации.