🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

VBA For Each en Excel — recorre una colección sin tocar un índice (y las trampas que esconde)

|

VBA For Each en Excel — recorre una colección sin tocar un índice (y las trampas que esconde)

TL;DRFor Each item In collection recorre cada miembro de una colección y te lo entrega de uno en uno — sin contador, sin .Count, sin desfases por uno. Úsalo cuando tienes una colección (un Range, las Worksheets, una matriz) y quieres tocar todos los elementos. Vuelve a For i = 1 To N solo cuando necesitas el índice en sí: para ir hacia atrás, para saltar con Step o para eliminar elementos sobre la marcha. Tres reglas evitan que te traicione: la variable de bucle debe ser un objeto o un Variant, recorrer una matriz es de solo lectura y nunca debes quitar elementos de la colección que estás recorriendo.

Dim cell As Range
Dim total As Double

For Each cell In Sheets("Data").Range("B2:B11")
    total = total + cell.Value      ' VBA te entrega cada celda por turno
Next cell

Debug.Print total                   ' sin i, sin .Count, sin límites que equivocar

La mayoría de los bucles de las macros reales dicen en el fondo lo mismo: «para cada celda de este rango…», «para cada hoja de este libro…», «para cada nombre de esta lista…». For Each es VBA dejándote escribir esa frase casi al pie de la letra. El For i = 1 To N basado en contador expresa la misma idea, pero te obliga a ti a gestionar el índice — valor inicial, valor final, el .Count, el desfase por uno. For Each te quita esa contabilidad. La pega es que te quita el índice por completo, y unas cuantas operaciones lo necesitan de verdad.

Lo que aprenderás

  • El modelo mental — For Each recorre pertenencia, no posiciones
  • Por qué la variable de bucle tiene que ser un Range, un Worksheet o un Variant — nunca un Long
  • La trampa de solo lectura: asignar a la variable de bucle de una matriz no hace nada
  • Por qué eliminar dentro de un For Each corrompe el recorrido en silencio — y qué hacer en su lugar
  • Una regla clara para cuándo elegir For Each y cuándo recurrir a For…Next

El modelo mental: For Each recorre pertenencia, no posiciones

Un bucle con contador piensa en posiciones: «ve a la casilla 1, luego a la 2, luego a la 3». For Each piensa en pertenencia: «dame cada cosa que pertenece a este grupo, en el orden en que el grupo las guarde». Nunca dices dónde está un elemento; simplemente lo recibes.

Ese cambio es la razón de que For Each sea más corto y, en rangos grandes, algo más rápido. Con Cells(i, 1) VBA tiene que calcular la dirección y resolver la celda en cada iteración. Con For Each cell In rng, el enumerador integrado de la colección te entrega cada vez un objeto Range ya resuelto — sin aritmética. Cambias el número de índice por un recorrido más limpio y rápido. Todo lo que sigue es consecuencia de no tener ese índice.

La regla que decide el tipo de la variable de bucle: objeto o Variant, nunca Long

Esto es lo primero con lo que tropieza la gente, porque es lo contrario de un bucle con contador. En For i = 1 To 10, i es un Long. En un For Each, la variable de bucle recibe un elemento entero de la colección, así que tiene que poder contenerlo:

' Rango de celdas -> el elemento es un Range
Dim cell As Range
For Each cell In Range("A1:A10")
    cell.Value = cell.Value * 2
Next cell

' Worksheets -> el elemento es un Worksheet
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
    Debug.Print ws.Name
Next ws

Declara Dim cell As Long para recorrer un rango y VBA lanza «Se requiere un objeto» (error 424) en cuanto intenta meter un Range en un número. La regla: el tipo de la variable de bucle debe coincidir con la clase de cosa que guarda la colecciónRange para celdas, Worksheet para hojas, Workbook para libros abiertos.

Hay una excepción, y es la que la gente olvida: las matrices. Una matriz de VBA puede contener cualquier cosa, así que el compilador no puede saber de antemano el tipo de los elementos. Recorrer una matriz con For Each exige un Variant:

Dim names As Variant
names = Array("Alice", "Bob", "Carol")

Dim n As Variant          ' DEBE ser Variant para recorrer una matriz
For Each n In names
    Debug.Print n
Next n

Usa ahí Dim n As String y obtendrás un error «No coinciden los tipos». Ante la duda con una matriz, la variable de bucle es un Variant.

La regla con la que todos caen una vez: recorrer una matriz es de solo lectura

Aquí tienes un error que parece que debería funcionar y no lo hace:

Dim nums As Variant
nums = Array(1, 2, 3)

Dim x As Variant
For Each x In nums
    x = x * 10            ' parece que multiplica cada elemento...
Next x

Debug.Print nums(0), nums(1), nums(2)   ' -> 1  2  3   (SIN CAMBIOS)

Multiplicaste x, no la matriz. Cuando For Each recorre una matriz, la variable de bucle es una copia del elemento, no una referencia viva a él. Asignar a x cambia la copia y la descarta en la siguiente iteración. La matriz no se mueve nunca.

Esto es lo más importante que hay que saber sobre For Each, porque falla en silencio — sin error, solo datos obsoletos. La solución es volver a un bucle con contador, que direcciona el elemento real por índice:

Dim i As Long
For i = LBound(nums) To UBound(nums)
    nums(i) = nums(i) * 10     ' ESTO sí cambia la matriz
Next i

Fíjate en la asimetría: recorrer un Range con For Each permite escribir (cell.Value = … llega, a través del objeto Range, hasta la celda real), porque cell es una referencia a objeto, no una copia. Son en concreto las matrices —y los elementos de tipo valor— las que son de solo lectura bajo For Each. Si tu bucle necesita reescribir el contenido de la matriz, usa For i = LBound To UBound.

La regla que se aprende por las malas: nunca elimines de la colección que estás recorriendo

For Each se apoya en el enumerador de la colección, y ese enumerador da por hecho que la colección no cambia de forma por debajo. Quita un elemento a mitad de recorrido y el enumerador pierde su sitio — salta elementos, o lanza un error, y nunca te dice cuál.

' ROTO — eliminar desplaza la colección bajo el enumerador
Dim sh As Worksheet
For Each sh In ThisWorkbook.Worksheets
    If sh.Name Like "Temp*" Then sh.Delete   ' salta hojas, impredecible
Next sh

La misma trampa salta cuando eliminas filas de una hoja, quitas elementos de una Collection o borras claves de un Dictionary dentro de un For Each. La regla es absoluta: si el bucle cambia la pertenencia de la colección, no uses For Each. Recórrela con un contador hacia atrás en su lugar, para que quitar un elemento nunca altere los índices que aún no has visitado:

' CORRECTO — cuenta hacia atrás para que las eliminaciones no desplacen lo que queda
Dim i As Long
For i = ThisWorkbook.Worksheets.Count To 1 Step -1
    With ThisWorkbook.Worksheets(i)
        If .Name Like "Temp*" Then .Delete
    End With
Next i

Es la misma disciplina de iteración inversa que usas para eliminar filas de una hoja con seguridad — mira el patrón Step -1 en VBA For Loop. La idea guía: For Each sirve para leer y modificar elementos en su sitio; un bucle con contador descendente sirve para añadirlos o quitarlos.

Cuándo usar For Each y cuándo For…Next

Ningún bucle es «mejor» — responden a preguntas distintas. Elige según lo que la tarea necesite del índice:

  • Usa For Each cuando tienes una colección y quieres tocar cada miembro en su orden natural: sumar un rango, dar formato a cada celda, imprimir el nombre de cada hoja, procesar cada elemento una vez. Más limpio de leer, sin límites que equivocar y más rápido en rangos grandes.
  • Usa For i = 1 To N cuando necesitas el índice como valor — para escribir en la fila i, para recorrer hacia atrás (Step -1), para saltar (Step 2), para comparar el elemento i con el i+1, o para eliminar sobre la marcha. Es además la única forma con escritura de reescribir los elementos de una matriz.

Una señal útil: si te descubres llamando a .Count y fabricando tu propio i solo para llegar a collection(i), seguramente querías For Each. Si estás yendo a por el elemento anterior o posterior al actual, o quitando elementos, necesitas el contador.

Cómo ayuda ExcelMaster

Elegir el bucle, tipar bien la variable de bucle, acordarte de que las matrices son de solo lectura y de que las eliminaciones deben ir hacia atrás — es justo la clase de criterio mecánico que se interpone entre «sé lo que quiero» y «la macro lo hace bien».

ExcelMaster te deja enunciar el objetivo en su lugar. Di «en la hoja Pedidos, resalta cada fila cuyo estado sea Vencido» o «elimina cada hoja cuyo nombre empiece por Temp», y escribe y ejecuta la lógica — eligiendo For Each donde un recorrido simple es lo correcto y un contador descendente donde se quitan elementos, para que el error de orden de eliminación nunca ocurra. Cuando escribas a mano una macro programada, seguirás eligiendo el bucle tú mismo. Para la tarea cotidiana de «hazme esto con los datos de hoy», describir el resultado gana a acertar a mano cada regla del enumerador.

Preguntas frecuentes

¿Cuál es la diferencia entre For Each y For…Next en VBA?

For Each recorre una colección por pertenencia — te entrega cada elemento en el orden natural de la colección y nunca gestionas un índice. For i = 1 To N es con contador — tú controlas el índice, así que puedes ir hacia atrás con Step -1, saltar con Step 2 o llegar al elemento i+1. Usa For Each para un recorrido directo de un Range, de las Worksheets o de una matriz; usa For…Next cuando necesites el índice, quieras eliminar elementos o tengas que reescribir los elementos de una matriz.

¿Por qué me sale «Se requiere un objeto» (error 424) en un bucle For Each?

Casi siempre porque la variable de bucle está declarada con el tipo equivocado — normalmente un Long o un String cuando la colección guarda objetos. For Each cell In Range(...) necesita Dim cell As Range, y For Each ws In Worksheets necesita Dim ws As Worksheet. Si recorres una matriz de VBA, la variable de bucle debe ser un Variant.

¿Puedo cambiar los valores de una matriz dentro de un bucle For Each?

No. Cuando For Each recorre una matriz, la variable de bucle es una copia de cada elemento, así que asignarle cambia la copia, no la matriz — sin ningún error. Para modificar la matriz, usa un bucle con contador: For i = LBound(arr) To UBound(arr) y asigna a arr(i). Recorrer un Range con For Each permite escribir, porque la variable de bucle es una referencia viva al objeto de la celda.

¿Cómo recorro cada celda de un rango con For Each?

Declara la variable de bucle como Range y recorre el rango directamente: For Each cell In Range("A1:B10"). VBA recorre las celdas fila a fila. Para un bloque grande esto es más rápido que anidar bucles Cells(i, j), pero el método más rápido de todos es leer el rango en una matriz de una sola vez (arr = Range("A1:B10").Value) y recorrer la matriz — mira VBA Array.

¿Por qué eliminar filas u hojas dentro de For Each se salta algunas?

Porque For Each depende de un enumerador que da por hecho que la pertenencia de la colección no cambia mientras la recorres. Eliminar un elemento desplaza todo lo que viene detrás, y el enumerador pierde su sitio — saltándose elementos en silencio. Nunca elimines dentro de un For Each. Usa un bucle con contador que cuente hacia atrás (For i = .Count To 1 Step -1) para que las eliminaciones no alteren los elementos a los que aún no has llegado.

Probado en

Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 01/08/2026.

Guías relacionadas: VBA For Loop · VBA Collection · VBA With · VBA Array · VBA Dictionary