Cómo iterar un bucle con índice y elemento en Swift


784

¿Hay una función que pueda usar para iterar sobre una matriz y tener tanto índice como elemento, como la enumeración de Python?

for index, element in enumerate(list):
    ...

Respuestas:


1662

Si. A partir de Swift 3.0, si necesita el índice para cada elemento junto con su valor, puede usar el enumerated()método para iterar sobre la matriz. Devuelve una secuencia de pares compuestos por el índice y el valor de cada elemento de la matriz. Por ejemplo:

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}

Antes de Swift 3.0 y después de Swift 2.0, la función se llamaba enumerate():

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}

Antes de Swift 2.0, enumerateera una función global.

for (index, element) in enumerate(list) {
    println("Item \(index): \(element)")
}

3
Aunque parece una tupla, en Swift 1.2, no estoy seguro acerca de 2.0, enumerate devuelve una estructura EnumerateSequence <base: SequenceType>.
nstein

2
@Leviathlon sobrecarga de rendimiento notable o medible que importará? No.
Dan Rosenstark

¿Es el acceso al índice el único beneficio de usar enumerate?
Miel

29
Tal vez cambien al gerundio, enumeratingpor Swift 4. ¡Emocionante!
Dan Rosenstark

3
@Honey for (index, element) incuando se usa enumeratedes engañoso. debería serfor (offset, element) in
Leo Dabus

116

Swift 5 proporciona un método llamado enumerated()para Array. enumerated()tiene la siguiente declaración:

func enumerated() -> EnumeratedSequence<Array<Element>>

Devuelve una secuencia de pares (n, x), donde n representa un número entero consecutivo que comienza en cero yx representa un elemento de la secuencia.


En los casos más simples, puede usar enumerated()con un bucle for. Por ejemplo:

let list = ["Car", "Bike", "Plane", "Boat"]
for (index, element) in list.enumerated() {
    print(index, ":", element)
}

/*
prints:
0 : Car
1 : Bike
2 : Plane
3 : Boat
*/

Sin embargo, tenga en cuenta que no está limitado a usar enumerated()con un bucle for. De hecho, si planea usar enumerated()un bucle for para algo similar al siguiente código, lo está haciendo mal:

let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()

for (index, element) in list.enumerated() {
    arrayOfTuples += [(index, element)]
}

print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]

Una forma más rápida de hacer esto es:

let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]

Como alternativa, también puede usar enumerated()con map:

let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]

Además, aunque tiene algunas limitaciones , forEachpuede ser un buen reemplazo para un bucle for:

let list = [Int](1...5)
list.reversed().enumerated().forEach { print($0, ":", $1) }

/*
prints:
0 : 5
1 : 4
2 : 3
3 : 2
4 : 1
*/

Al usar enumerated()y makeIterator(), incluso puede iterar manualmente en su Array. Por ejemplo:

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    var generator = ["Car", "Bike", "Plane", "Boat"].enumerated().makeIterator()

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .system)
        button.setTitle("Tap", for: .normal)
        button.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        button.addTarget(self, action: #selector(iterate(_:)), for: .touchUpInside)
        view.addSubview(button)
    }

    @objc func iterate(_ sender: UIButton) {
        let tuple = generator.next()
        print(String(describing: tuple))
    }

}

PlaygroundPage.current.liveView = ViewController()

/*
 Optional((offset: 0, element: "Car"))
 Optional((offset: 1, element: "Bike"))
 Optional((offset: 2, element: "Plane"))
 Optional((offset: 3, element: "Boat"))
 nil
 nil
 nil
 */

1
¿Es el acceso al índice el único beneficio de usar enumerate?
Miel

52

Comenzando con Swift 2, la función de enumeración debe llamarse en la colección de la siguiente manera:

for (index, element) in list.enumerate() {
    print("Item \(index): \(element)")
}

47

Encontré esta respuesta mientras buscaba una manera de hacerlo con un Diccionario , y resulta que es bastante fácil adaptarlo, solo pasa una tupla para el elemento.

// Swift 2

var list = ["a": 1, "b": 2]

for (index, (letter, value)) in list.enumerate() {
    print("Item \(index): \(letter) \(value)")
}

18

Simplemente puede usar el ciclo de enumeración para obtener el resultado deseado:

Swift 2:

for (index, element) in elements.enumerate() {
    print("\(index): \(element)")
}

Swift 3 y 4:

for (index, element) in elements.enumerated() {
    print("\(index): \(element)")
}

O simplemente puede pasar por un bucle for para obtener el mismo resultado:

for index in 0..<elements.count {
    let element = elements[index]
    print("\(index): \(element)")
}

Espero eso ayude.


14

Enumeración básica

for (index, element) in arrayOfValues.enumerate() {
// do something useful
}

o con Swift 3 ...

for (index, element) in arrayOfValues.enumerated() {
// do something useful
}

Enumerar, filtrar y mapear

Sin embargo, con mayor frecuencia uso enumerate en combinación con map o filter. Por ejemplo, con operar en un par de matrices.

En esta matriz, quería filtrar elementos indexados impares o pares y convertirlos de Ints a Dobles. Entonces enumerate()obtiene su índice y el elemento, luego el filtro verifica el índice y, finalmente, para deshacerme de la tupla resultante, lo mapeo solo al elemento.

let evens = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 == 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })
let odds = arrayOfValues.enumerate().filter({
                            (index: Int, element: Int) -> Bool in
                            return index % 2 != 0
                        }).map({ (_: Int, element: Int) -> Double in
                            return Double(element)
                        })

9

Usando .enumerate()trabajos, pero no proporciona el índice verdadero del elemento; solo proporciona un Int que comienza con 0 y se incrementa en 1 para cada elemento sucesivo. Esto suele ser irrelevante, pero existe la posibilidad de un comportamiento inesperado cuando se usa con el ArraySlicetipo. Toma el siguiente código:

let a = ["a", "b", "c", "d", "e"]
a.indices //=> 0..<5

let aSlice = a[1..<4] //=> ArraySlice with ["b", "c", "d"]
aSlice.indices //=> 1..<4

var test = [Int: String]()
for (index, element) in aSlice.enumerate() {
    test[index] = element
}
test //=> [0: "b", 1: "c", 2: "d"] // indices presented as 0..<3, but they are actually 1..<4
test[0] == aSlice[0] // ERROR: out of bounds

Es un ejemplo un tanto artificial, y no es un problema común en la práctica, pero creo que vale la pena saber que esto puede suceder.


2
it does not actually provide the true index of the element; it only provides an Int beginning with 0 and incrementing by 1 for each successive elementSí, por eso se llama enumerar . Además, el segmento no es una matriz, por lo que no sorprende que se comporte de manera diferente. No hay ningún error aquí: todo es por diseño. :)
Eric Aya

55
Es cierto, pero nunca lo llamé un error. Simplemente es un comportamiento potencialmente inesperado que pensé que valía la pena mencionar para aquellos que no sabían cómo podría interactuar negativamente con el tipo ArraySlice.
Cole Campbell

¿Conoce alguna forma de obtener el índice del elemento real, por ejemplo, si se usa filterprimero?
Alexandre Cassagne

9

Comenzando con Swift 3, es

for (index, element) in list.enumerated() {
  print("Item \(index): \(element)")
}

9

Para completar, simplemente puede iterar sobre los índices de su matriz y usar el subíndice para acceder al elemento en el índice correspondiente:

let list = [100,200,300,400,500]
for index in list.indices {
    print("Element at:", index, " Value:", list[index])
}

Usando para cada

list.indices.forEach {
    print("Element at:", $0, " Value:", list[$0])
}

Usando el enumerated()método de recolección . Tenga en cuenta que devuelve una colección de tuplas con el offsety el element:

for item in list.enumerated() {
    print("Element at:", item.offset, " Value:", item.element)
}

utilizando forEach:

list.enumerated().forEach {
    print("Element at:", $0.offset, " Value:", $0.element)
}

Esos imprimirán

Elemento en: 0 Valor: 100

Elemento en: 1 Valor: 200

Elemento en: 2 Valor: 300

Elemento en: 3 Valor: 400

Elemento en: 4 Valor: 500

Si necesita el índice de matriz (no el desplazamiento) y su elemento, puede extender la Colección y crear su propio método para obtener los elementos indexados:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        var index = startIndex
        for element in self {
            try body((index,element))
            formIndex(after: &index)
        }
    }
}

Otra posible implementación según lo sugerido por Alex es comprimir los índices de colección con sus elementos:

extension Collection {
    func indexedElements(body: ((index: Index, element: Element)) throws -> Void) rethrows {
        for element in zip(indices, self) { try body(element) }
    }
}

Pruebas:

let list =  ["100","200","300","400","500"]
list.dropFirst(2).indexedElements {
    print("Index:", $0.index, "Element:", $0.element)
}

Esto p [rint

Índice: 2 Elemento: 300

Índice: 3 Elemento: 400

Índice: 4 Elemento: 500


Por cierto, puedes implementar enumeratedIndiceshaciendo un bucle conzip(self.indices, self)
Alexander - Restablecer Mónica

@ Alexander-ReinstateMonica for element in zip(indices, self) { try body(element) }. Por cierto, no me gusta el nombre que elegí, indexedElementspodría describir mejor lo que hace
Leo Dabus

Ooo creo que ese es un mejor nombre. Sí, un forbucle funciona, pero tambiénzip(self.indices, self) .forEach(body)
Alexander - Restablece a Monica

@ Alexander-ReinstateMonica forEachhace un bucle for detrás de escena. Prefiero mantenerlo simple github.com/apple/swift/blob/master/stdlib/public/core/… @inlinable public func forEach( _ body: (Element) throws -> Void ) rethrows { for element in self { try body(element) } } }
Leo Dabus

7

Esta es la fórmula del bucle de enumeración:

for (index, value) in shoppingList.enumerate() {
print("Item \(index + 1): \(value)")
}

Para más detalles puede consultar aquí .


5

Personalmente prefiero usar el forEachmétodo:

list.enumerated().forEach { (index, element) in
    ...
}

También puedes usar la versión corta:

list.enumerated().forEach { print("index: \($0.0), value: \($0.1)") }

4

Xcode 8 y Swift 3: la matriz se puede enumerar usando tempArray.enumerated()

Ejemplo:

var someStrs = [String]()

someStrs.append("Apple")  
someStrs.append("Amazon")  
someStrs += ["Google"]    


for (index, item) in someStrs.enumerated()  
{  
        print("Value at index = \(index) is \(item)").  
}

consola:

Value at index = 0 is Apple
Value at index = 1 is Amazon
Value at index = 2 is Google

3

Para aquellos que quieran usar forEach.

Swift 4

extension Array {
  func forEachWithIndex(_ body: (Int, Element) throws -> Void) rethrows {
    try zip((startIndex ..< endIndex), self).forEach(body)
  }
}

O

array.enumerated().forEach { ... }

2

Para lo que desea hacer, debe usar el enumerated()método en su matriz :

for (index, element) in list.enumerated() {
    print("\(index) - \(element)")
}

-1

Llamamos a la función enumerate para implementar esto. me gusta

for (index, element) en array.enumerate () {

// index es indexposition de la matriz

// elemento es elemento de la matriz

}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.