Saltar al contenido
Curso de JavaScriptModerno · Interactivo

Unidad 11 · Lecciones 080-087

Iteradores en JavaScript

Una estructura de control decide si se ejecuta un codigo. Un iterador decide cuantas veces. Mostrar los comentarios de una publicacion, listar los productos del carrito, recorrer los tweets que te perdiste: todo eso es repetir codigo hasta que ya no quede nada que mostrar.

📚 8 lecciones originales ⏱ Lectura ~35 min ↩ Requiere: Unidades 8, 9 y 10

🎯 Objetivo de aprendizaje

Al terminar conoceras los seis iteradores del lenguaje, sabras cual usar en cada situacion, podras cortar o saltar vueltas con break y continue, y habras resuelto el ejercicio FizzBuzz.

11.1 El for loop

El iterador clasico, presente en casi todos los lenguajes con una sintaxis muy parecida. Lo usaste en la Unidad 8; aqui lo analizamos a fondo.

Las tres partes del for
for (let i = 0; i < 10; i++) {
    console.log(`Numero: ${i}`);
}
#ParteCodigoFuncion
1Inicializadorlet i = 0Desde donde empieza a contar.
2Condicioni < 10Mientras se cumpla, sigue repitiendo.
3Incrementoi++Como avanza en cada vuelta.

💡 Por que se llama i

Por convencion: viene de indice. No le pongas indice ni contador; en cualquier equipo del mundo se espera ver i.

El bucle anterior imprime del 0 al 9. Al llegar a 10, la condicion 10 < 10 ya no se cumple y se detiene.

Cambiando el incremento
// De dos en dos
for (let i = 0; i < 10; i += 2) {
    console.log(i);
}

console.log('---');

// Llegar hasta el 10 incluido
for (let i = 0; i <= 10; i++) {
    console.log(i);
}

11.2 Ejercicio clasico: pares e impares

🎓 Pregunta habitual para perfiles junior

¿Como identificas los numeros pares y los impares dentro de un bucle? Con el modulo que aprendiste en la Unidad 4: todo numero divisible entre 2 con residuo 0 es par.

Pares e impares
for (let i = 1; i < 20; i++) {
    if (i % 2 === 0) {
        console.log(`El numero ${i} es par`);
    } else {
        console.log(`El numero ${i} es impar`);
    }
}

Fijate en como se combinan tres unidades distintas: el bucle (11), el condicional (10) y el operador modulo (4). A partir de aqui casi todo el codigo mezcla varios conceptos.

11.3 break y continue

break
Rompe el bucle entero. Sale inmediatamente, aunque la condicion siguiera cumpliendose.
continue
Salta solo la vuelta actual. No ejecuta el resto del cuerpo, pero el bucle continua.
break: se detiene en el 5
for (let i = 0; i < 10; i++) {
    if (i === 5) {
        console.log('Este es el 5');
        break;
    }
    console.log(`Numero: ${i}`);
}
continue: sustituye el 5 y sigue
for (let i = 0; i < 10; i++) {
    if (i === 5) {
        console.log('Cinco');
        continue;
    }
    console.log(`Numero: ${i}`);
}

Un uso real de continue

Marcar el producto del carrito que tenga descuento, sin que se imprima dos veces:

Destacar un producto en oferta
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100, descuento: true },
    { nombre: 'Celular', precio: 800 }
];

for (let i = 0; i < carrito.length; i++) {

    if (carrito[i].descuento) {
        console.log(`El articulo ${carrito[i].nombre} tiene descuento`);
        continue;   // ya lo mostramos, saltamos a la siguiente vuelta
    }

    console.log(carrito[i].nombre);
}

💡 Fijate en el if

Se escribe if (carrito[i].descuento), no === true. Es exactamente la buena practica de la Unidad 6.

11.4 Actividad integradora: FizzBuzz

🎯 El enunciado

Recorre los numeros del 1 al 100 y, para cada uno:

  • Si es multiplo de 3 → imprime Fizz
  • Si es multiplo de 5 → imprime Buzz
  • Si es multiplo de 3 y de 5 → imprime FizzBuzz

Es el ejercicio de entrevista mas famoso del mundo y mucha gente lo suspende. No por dificil, sino por un detalle de orden. Vamos paso a paso, que es la forma correcta de atacar cualquier problema que no sepas resolver de golpe.

Paso 1: generar los numeros

Lo mas sencillo primero
for (let i = 1; i <= 20; i++) {
    console.log(i);
}

Paso 2: detectar los multiplos de 3

El modulo otra vez
for (let i = 1; i <= 20; i++) {
    if (i % 3 === 0) {
        console.log('Fizz');
    } else {
        console.log(i);
    }
}

Paso 3: anadir los multiplos de 5

Casi... pero el 15 falla
for (let i = 1; i <= 20; i++) {
    if (i % 3 === 0) {
        console.log('Fizz');
    } else if (i % 5 === 0) {
        console.log('Buzz');
    } else {
        console.log(i);
    }
}

✗ Aqui es donde falla casi todo el mundo

El 15 imprime Fizz en lugar de FizzBuzz. ¿Por que? Porque un if ejecuta solo la primera condicion que se cumple (Unidad 10), y 15 es multiplo de 3, asi que entra ahi y nunca llega a comprobar nada mas.

Anadir un tercer else if al final tampoco lo arregla, por el mismo motivo.

Paso 4: la solucion

La condicion de FizzBuzz es la menos frecuente de las tres, asi que tiene que ir primero:

FizzBuzz resuelto
for (let i = 1; i <= 30; i++) {

    if (i % 15 === 0) {
        console.log('FizzBuzz');
    } else if (i % 3 === 0) {
        console.log('Fizz');
    } else if (i % 5 === 0) {
        console.log('Buzz');
    } else {
        console.log(i);
    }
}
Ver una solucion alternativa (sin el 15)

En programacion casi siempre hay varias formas de resolver lo mismo. Esta usa &&:

Version con AND
for (let i = 1; i <= 30; i++) {

    if (i % 3 === 0 && i % 5 === 0) {
        console.log('FizzBuzz');
    } else if (i % 3 === 0) {
        console.log('Fizz');
    } else if (i % 5 === 0) {
        console.log('Buzz');
    } else {
        console.log(i);
    }
}

Es equivalente: un numero divisible entre 3 y entre 5 es exactamente un multiplo de 15. Esta version es un poco mas explicita sobre la intencion.

✓ La leccion de fondo

Cuando un problema te supera, divide y avanza por pasos: primero los numeros, luego una condicion, luego la otra, y al final el caso especial. Esa es la habilidad que realmente se evalua en una entrevista.

11.5 while

Tiene las mismas tres partes que el for, pero repartidas en tres lugares distintos. Se ejecuta mientras la condicion sea verdadera.

Estructura del while
let i = 0;                      // 1. inicializador, FUERA del bucle

while (i < 10) {                // 2. condicion, en el parentesis
    console.log(`Numero ${i}`);
    i++;                        // 3. incremento, al final del cuerpo
}

✗ El peligro del while

Si olvidas el incremento, el bucle no termina nunca y bloquea el navegador. Es el error mas comun con while. El for lo evita porque el incremento forma parte de su propia sintaxis.

Si la condicion no se cumple, no se ejecuta nunca
let i = 100;

while (i < 10) {
    console.log(`Numero ${i}`);
    i++;
}

console.log('El while no imprimio nada');

11.6 do while

Igual que el while, con una diferencia importante: se ejecuta al menos una vez, porque comprueba la condicion despues de ejecutar el cuerpo.

Estructura del do while
let i = 0;

do {
    console.log(`Numero ${i}`);
    i++;
} while (i < 10);
La diferencia, en accion
let i = 1000;   // la condicion NO se cumple desde el principio

do {
    console.log(`Numero ${i}`);   // aun asi, se ejecuta una vez
    i++;
} while (i < 10);

🎓 Pregunta de entrevista

¿Cual es la diferencia entre while y do while?
while comprueba la condicion antes, asi que puede no ejecutarse ni una sola vez. do while la comprueba despues, por lo que su cuerpo se ejecuta siempre al menos una vez.

11.7 forEach y map: los iteradores de JavaScript

for, while y do while existen en casi todos los lenguajes. Pero JavaScript tiene los suyos propios, pensados especificamente para arreglos, y son los que mas usaras.

forEach sobre un arreglo simple
const pendientes = ['Tarea', 'Comer', 'Proyecto', 'Estudiar JavaScript'];

pendientes.forEach(pendiente => console.log(pendiente));

El segundo parametro: el indice

forEach te pasa el indice automaticamente
const pendientes = ['Tarea', 'Comer', 'Proyecto', 'Estudiar JavaScript'];

pendientes.forEach((pendiente, index) => {
    console.log(`${index}: ${pendiente}`);
});

💡 Los nombres los eliges tu

pendiente e index son nombres inventados por ti. Lo que importa es el orden: el primer parametro es siempre el elemento y el segundo siempre el indice.

La diferencia con map, otra vez

map llena un arreglo nuevo; forEach no
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 }
];

const nuevoArreglo = carrito.map(producto => `${producto.nombre} - ${producto.precio}`);
const nuevoArreglo2 = carrito.forEach(producto => `${producto.nombre} - ${producto.precio}`);

console.log(nuevoArreglo);    // el arreglo con los textos
console.log(nuevoArreglo2);   // undefined

11.8 for...of: la version simplificada

Recorre un arreglo sin contadores, sin length y sin indices. Es la forma mas legible de recorrer un arreglo de principio a fin.

Arreglo simple
const pendientes = ['Tarea', 'Comer', 'Proyecto', 'Estudiar JavaScript'];

for (let pendiente of pendientes) {
    console.log(pendiente);
}

Como leerlo

«Para cada pendiente de pendientes». En cada vuelta, la variable que tu nombras recibe el elemento actual. Cuando el arreglo se acaba, el bucle se detiene solo.

Arreglo de objetos
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 800 }
];

for (let producto of carrito) {
    console.log(`${producto.nombre}: ${producto.precio}`);
}

Compara con el for equivalente de la Unidad 8: mismo resultado, mitad de ruido.

11.9 for...in: recorrer objetos

⚠ La confusion mas comun

for...of y for...in se escriben casi igual, pero no hacen lo mismo: for...of recorre arreglos; for...in recorre objetos.

for...in sobre un arreglo: devuelve los indices
const pendientes = ['Tarea', 'Comer', 'Proyecto'];

for (let pendiente in pendientes) {
    console.log(pendiente);   // 0, 1, 2  <-- las posiciones, no los valores
}
for...in sobre un objeto: su uso correcto
const automovil = {
    modelo: 'Camaro',
    year: 1969,
    motor: '6.0'
};

// Las LLAVES del objeto
for (let propiedad in automovil) {
    console.log(propiedad);
}

console.log('--- ahora los valores ---');

// Los VALORES: se accede con corchetes, porque el nombre esta en una variable
for (let propiedad in automovil) {
    console.log(`${propiedad}: ${automovil[propiedad]}`);
}

💡 Aqui aparece la sintaxis de corchetes

En la Unidad 7 viste que se podia acceder con objeto['llave']. Este es el caso donde es imprescindible: el nombre de la propiedad esta dentro de una variable, asi que automovil.propiedad buscaria una llave llamada literalmente "propiedad" y no funcionaria.

La alternativa moderna: Object.entries()

Desde ECMAScript 2017 hay una forma mas comoda de recorrer un objeto: convertirlo en pares [llave, valor] y recorrerlos con for...of y destructuring de arreglos.

for...of + Object.entries + destructuring
const automovil = {
    modelo: 'Camaro',
    year: 1969,
    motor: '6.0'
};

for (let [llave, valor] of Object.entries(automovil)) {
    console.log(`${llave} -> ${valor}`);
}

Aqui se combinan tres cosas que ya conoces: Object.entries() (Unidad 7), el destructuring de arreglos (Unidad 8) y for...of (seccion anterior).

11.10 ¿Cual uso en cada caso?

IteradorRecorreUsalo cuando
forCualquier cosa contableNecesitas control total del contador o saltar de N en N.
whileHasta que algo deje de cumplirseNo sabes de antemano cuantas vueltas daras.
do whileIgual que whileEl cuerpo debe ejecutarse al menos una vez si o si.
forEachArreglosSolo quieres hacer algo con cada elemento.
mapArreglosQuieres transformar el arreglo en uno nuevo.
for...ofArreglosQuieres la sintaxis mas limpia y no necesitas el indice.
for...inObjetosNecesitas recorrer las propiedades de un objeto.

💡 En la practica

En codigo JavaScript moderno veras sobre todo forEach, map y for...of. El for clasico sigue siendo util, y while / do while aparecen menos, pero se preguntan en entrevistas.

11.11 Ejercicios practicos

Ejercicio 1

Pares e impares con while

Reescribe el ejercicio de pares e impares de la seccion 11.2 usando un while en lugar de un for.

Este ejercicio se propone explicitamente en el material original del curso.

Ver solucion
Solucion
let i = 1;

while (i < 20) {
    if (i % 2 === 0) {
        console.log(`El numero ${i} es par`);
    } else {
        console.log(`El numero ${i} es impar`);
    }
    i++;   // sin esta linea el bucle no termina nunca
}
Ejercicio 2

Total del carrito

Recorre un carrito de cuatro productos y calcula el total a pagar. Hazlo dos veces: con for...of y con forEach.

Ver solucion
Solucion
const carrito = [
    { nombre: 'Monitor', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 800 },
    { nombre: 'Teclado', precio: 50 }
];

// Con for...of
let total1 = 0;
for (let producto of carrito) {
    total1 += producto.precio;
}
console.log('Total con for...of:', total1);

// Con forEach
let total2 = 0;
carrito.forEach(producto => total2 += producto.precio);
console.log('Total con forEach:', total2);

En la Unidad 12 veras que existe un metodo, reduce, disenado exactamente para esto.

Reto 1

Buscar y parar

Recorre un arreglo de usuarios buscando uno concreto por su email. En cuanto lo encuentres, muestralo y detén el bucle para no seguir recorriendo innecesariamente.

Ver solucion
Solucion
const usuarios = [
    { nombre: 'Ana', email: 'ana@correo.com' },
    { nombre: 'Luis', email: 'luis@correo.com' },
    { nombre: 'Marta', email: 'marta@correo.com' },
    { nombre: 'Pedro', email: 'pedro@correo.com' }
];

const buscado = 'marta@correo.com';

for (let i = 0; i < usuarios.length; i++) {
    console.log(`Revisando la posicion ${i}...`);

    if (usuarios[i].email === buscado) {
        console.log(`Encontrado: ${usuarios[i].nombre} en la posicion ${i}`);
        break;   // no seguimos: ya lo tenemos
    }
}

Sin break, el bucle revisaria tambien a Pedro sin necesidad. Con arreglos pequenos da igual, pero con miles de registros importa.

Reto 2

FizzBuzz con while

Resuelve FizzBuzz otra vez, pero usando un while en vez de un for. Comprueba que el resultado es identico.

Ver solucion
Solucion
let i = 1;

while (i <= 30) {

    if (i % 15 === 0) {
        console.log('FizzBuzz');
    } else if (i % 3 === 0) {
        console.log('Fizz');
    } else if (i % 5 === 0) {
        console.log('Buzz');
    } else {
        console.log(i);
    }

    i++;
}

La logica de las condiciones es exactamente la misma: lo unico que cambia es el mecanismo de repeticion. Cualquier iterador sirve para resolver cualquier problema de este tipo.

11.12 Resumen de lo aprendido

  • Un iterador repite codigo hasta que una condicion deja de cumplirse.
  • for tiene tres partes: inicializador, condicion e incremento, todas en el parentesis.
  • break rompe el bucle entero; continue salta solo la vuelta actual.
  • FizzBuzz se resuelve poniendo la condicion menos frecuente primero, porque un if solo ejecuta la primera que se cumple.
  • while reparte las tres partes; si olvidas el incremento, bucle infinito.
  • do while ejecuta el cuerpo al menos una vez, porque comprueba la condicion al final.
  • forEach recorre arreglos y te pasa el elemento y (opcionalmente) el indice. map hace lo mismo pero devuelve un arreglo nuevo.
  • for...of recorre arreglos sin contadores. for...in recorre objetos, y sus valores se leen con corchetes.
  • Object.entries() + for...of + destructuring es la forma moderna de recorrer un objeto.

11.13 Verificacion de cierre

En FizzBuzz, ¿por que hay que comprobar primero los multiplos de 15?

  • Porque 15 es mayor que 3 y que 5
  • Porque el modulo de 15 es mas rapido de calcular
  • Porque el if solo ejecuta la primera condicion que se cumple
  • No hace falta: el orden da igual

¿Cual es la diferencia entre while y do while?

  • do while se ejecuta al menos una vez aunque la condicion no se cumpla
  • do while solo funciona con arreglos
  • while es mas rapido
  • Ninguna: es la misma sintaxis escrita al reves

¿Que devuelve for (let x in ['a','b','c']) en cada vuelta?

  • Los valores 'a', 'b', 'c'
  • El arreglo completo tres veces
  • Un error, porque for...in no admite arreglos
  • Los indices 0, 1, 2

🏁 Unidad completada