¿Cómo obtener el índice de un elemento al recorrer un arreglo en Swift? Analizando una pregunta popular
\n\nImagine: está escribiendo en Swift y no solo quiere recorrer un arreglo, sino también saber en qué posición se encuentra cada elemento. Ya sea que necesite modificar elementos por índice, comparar valores adyacentes o simplemente mostrar una lista numerada, la tarea es la misma. La pregunta "How to iterate a loop with index and element in Swift" ha acumulado miles de visitas en Stack Overflow. Esto significa que este tema preocupa a muchos.
\n\nEn este artículo no solo daremos una respuesta preparada. Miraremos bajo el capó: analizaremos qué métodos existen, cuál es más rápido, cuál es más seguro y por qué uno de ellos — enumerated() — se ha convertido en el estándar de facto. ¡Allá vamos!
El problema: ¿por qué el simple for-in no es suficiente?
\n\nSupongamos que tiene un arreglo de cadenas:
\n\nlet fruits = ["Manzana", "Banana", "Cereza", "Melón"]\n\nLa forma más obvia es for-in:
for fruit in fruits {\n print(fruit)\n}\n\nEsto imprimirá los nombres de las frutas. Pero, ¿qué pasa si necesita saber el índice? Por ejemplo: "1. Manzana, 2. Banana..."? El simple for-in no proporciona el índice. Hay que inventar soluciones improvisadas:
var index = 0\nfor fruit in fruits {\n print("\\(index). \\(fruit)")\n index += 1\n}\n\n¿Funciona? Sí. ¿Es bonito? No. ¿Y si el arreglo está vacío? ¿Y si accidentalmente olvida incrementar index? Bienvenido al mundo de los errores. Swift ofrece soluciones más elegantes.
Método n.º 1: El clásico — método enumerated()
\n\nEsta es la forma más popular y recomendada. El método enumerated() devuelve una secuencia de tuplas (offset: Int, element: Element). Veamos cómo funciona:
for (index, fruit) in fruits.enumerated() {\n print("\\(index + 1). \\(fruit)")\n}\n\nSalida:
\n1. Manzana\n2. Banana\n3. Cereza\n4. Melón\n\n¿Qué sucede bajo el capó?
\n\nenumerated() no crea un nuevo arreglo. Devuelve una secuencia perezosa de tipo EnumeratedSequence. Esto significa que los elementos no se copian a una nueva estructura de datos, sino que se "envuelven" sobre la marcha. Cada vez que se accede al siguiente elemento, la secuencia simplemente incrementa el contador y devuelve una tupla. Esto es muy eficiente en términos de memoria, especialmente para colecciones grandes.
Un matiz importante: offset siempre comienza en 0. Si necesita un índice comenzando en 1 (como en la numeración de listas), simplemente sume uno. Swift no lo hace por usted; esta es una decisión consciente de los desarrolladores del lenguaje para mantener la uniformidad con la indexación de arreglos.
enumerated() con forEach
\n\nEl método forEach también admite enumerated():
fruits.enumerated().forEach { (index, fruit) in\n print("\\(index). \\(fruit)")\n}\n\nLa diferencia entre for-in y forEach es importante: dentro de forEach no se puede usar break o continue. Si necesita salir del bucle antes de tiempo, use for-in.
Método n.º 2: Indexación mediante rango (Range)
\n\nUna forma antigua, pero que aún se encuentra, es iterar sobre los índices:
\n\nfor index in 0..<fruits.count {\n print("\\(index). \\(fruits[index])")\n}\n\nParece sencillo. Pero hay trampas:
\n\n- \n
- Seguridad: Si el arreglo cambia durante la iteración (por ejemplo, en código multiproceso),
fruits.countpuede quedar desactualizado y se saldrá de los límites del arreglo. \n - Legibilidad: El código parece más de "bajo nivel". Otros desarrolladores perderán un segundo extra para entender lo que está haciendo. \n
- Rendimiento: El acceso por índice
fruits[index]es una operación O(1) para arreglos, pero para otros