TL;DR —
For Each item In collectionrecorre 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 (unRange, lasWorksheets, una matriz) y quieres tocar todos los elementos. Vuelve aFor i = 1 To Nsolo cuando necesitas el índice en sí: para ir hacia atrás, para saltar conStepo para eliminar elementos sobre la marcha. Tres reglas evitan que te traicione: la variable de bucle debe ser un objeto o unVariant, 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 Eachrecorre pertenencia, no posiciones - Por qué la variable de bucle tiene que ser un
Range, unWorksheeto unVariant— nunca unLong - La trampa de solo lectura: asignar a la variable de bucle de una matriz no hace nada
- Por qué eliminar dentro de un
For Eachcorrompe el recorrido en silencio — y qué hacer en su lugar - Una regla clara para cuándo elegir
For Eachy cuándo recurrir aFor…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ón — Range 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 sí 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 Eachcuando 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 Ncuando necesitas el índice como valor — para escribir en la filai, para recorrer hacia atrás (Step -1), para saltar (Step 2), para comparar el elementoicon eli+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 sí 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 sí 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
