Saltar al contenido
Curso de JavaScriptModerno · Interactivo

Unidad 12 · Lecciones 088-095

Array Methods

Ya sabes recorrer un arreglo a mano. Ahora vas a aprender los metodos que hacen el trabajo pesado por ti: buscar, filtrar, sumar, comprobar y combinar, cada uno en una sola linea. Es lo que hay detras de los buscadores con filtros de Amazon o Airbnb.

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

🎯 Objetivo de aprendizaje

Al terminar sabras elegir y usar el array method adecuado para comprobar si algo existe, encontrar su posicion, filtrar por criterios, acumular un total y unir arreglos, sin escribir bucles manuales.

12.1 El punto de partida

Toda la unidad trabaja con estos dos arreglos, que representan los dos escenarios que encontraras siempre: un arreglo plano y un arreglo de objetos.

Los dos arreglos de referencia
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo'];

const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Audifonos', precio: 300 },
    { nombre: 'Teclado', precio: 400 },
    { nombre: 'Celular', precio: 700 }
];

💡 El patron que comparten todos

Casi todos estos metodos siguen la misma forma: arreglo.metodo(elemento => condicion). Reciben una arrow function (Unidad 9) a la que se le pasa automaticamente cada elemento. Cuando el cuerpo es de una linea, el return es implicito y los parentesis del parametro sobran.

12.2 ¿Existe este valor? includes y some

La forma manual, para comparar

Buscando con forEach
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo'];

meses.forEach(mes => {
    if (mes === 'Enero') {
        console.log('Enero si existe');
    }
});

.includes() — solo para arreglos planos

Una sola linea
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo'];

console.log(meses.includes('Enero'));      // true
console.log(meses.includes('Diciembre'));  // false

⚠ La limitacion de includes

Funciona con arreglos de valores simples. No sirve para arreglos de objetos, porque no puede saber por que propiedad quieres buscar.

.some() — sirve para los dos casos

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

// Version larga, para entenderla
const existe = carrito.some(function(producto) {
    return producto.nombre === 'Celular';
});

// Version corta con arrow function y return implicito
const existe2 = carrito.some(producto => producto.nombre === 'Celular');
const noExiste = carrito.some(producto => producto.nombre === 'Monitor curvo');

console.log(existe);     // true
console.log(existe2);    // true
console.log(noExiste);   // false
some tambien funciona con arreglos planos
const meses = ['Enero', 'Febrero', 'Marzo'];

console.log(meses.some(mes => mes === 'Febrero'));   // true

💡 Cual usar

Ambos devuelven true o false. includes es mas corto pero solo vale para valores simples; some vale para todo. Con arreglos de objetos, siempre some.

12.3 ¿En que posicion esta? findIndex

Encontrar el indice
const meses = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo'];

const indice = meses.findIndex(mes => mes === 'Abril');
console.log(indice);   // 3

const noExiste = meses.findIndex(mes => mes === 'Diciembre');
console.log(noExiste); // -1

💡 El -1 otra vez

Igual que indexOf en los strings (Unidad 3), findIndex devuelve -1 cuando no encuentra nada. Por eso se suele comprobar asi:

Comprobar el resultado
const meses = ['Enero', 'Febrero', 'Marzo'];
const indice = meses.findIndex(mes => mes === 'Diciembre');

if (indice >= 0) {
    console.log(`Encontrado en la posicion ${indice}`);
} else {
    console.log('No se encontro');
}
Con arreglos de objetos
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Audifonos', precio: 100 }
];

const indice = carrito.findIndex(producto => producto.precio === 100);

console.log(indice);   // 1  <-- solo el PRIMERO que coincide

⚠ Solo devuelve el primero

Aunque haya dos productos a 100, findIndex se detiene en el primero. Si necesitas todos los que cumplan la condicion, el metodo es filter (seccion 12.5).

12.4 reduce: de muchos valores a uno solo

Su nombre asusta y su definicion oficial («funcion reductora») no ayuda. En la practica es simple: toma muchos datos y los reduce a un unico resultado. El caso tipico: el total del carrito.

La forma manual

Con forEach y una variable externa
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 700 }
];

let total = 0;
carrito.forEach(producto => total += producto.precio);

console.log(`Total a pagar: ${total}`);

Con reduce

Sin variables externas
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 700 }
];

const resultado = carrito.reduce(
    (total, producto) => total + producto.precio,   // que hacer en cada vuelta
    0                                               // valor inicial
);

console.log(`Total a pagar: ${resultado}`);

⚠ reduce es distinto a los demas

Todos los metodos anteriores reciben el elemento actual como primer parametro. reduce no: el primero es el acumulador (lo que llevas sumado) y el elemento actual es el segundo. Es la causa habitual de confusion.

Que hace cada parte

ElementoQue es
totalEl acumulador: el valor que se va arrastrando de una vuelta a la siguiente.
productoEl elemento actual del arreglo.
total + producto.precioLo que se devuelve; sera el total de la siguiente vuelta. Fijate en que es + y no +=: el acumulador ya conserva el valor por si mismo.
0El valor inicial, despues de la funcion. Se asigna a total en la primera vuelta.
El valor inicial no tiene por que ser 0
const carrito = [
    { nombre: 'Monitor', precio: 500 },
    { nombre: 'Tablet', precio: 100 }
];

console.log(carrito.reduce((total, p) => total + p.precio, 0));      // 600
console.log(carrito.reduce((total, p) => total + p.precio, 2000));   // 2600 (gastos previos)

12.5 filter: el que mas usaras

filter crea un arreglo nuevo con todos los elementos que cumplan la condicion. Es la base de cualquier buscador con filtros.

Filtrar por precio
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Audifonos', precio: 300 },
    { nombre: 'Teclado', precio: 400 },
    { nombre: 'Celular', precio: 700 }
];

// Todos los que cuestan mas de 400
const caros = carrito.filter(producto => producto.precio > 400);
console.log(caros);

// Todos los que cuestan menos de 600
const baratos = carrito.filter(producto => producto.precio < 600);
console.log(baratos);

El caso estrella: eliminar del carrito

Quitar un producto sin tocar el original
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Audifonos', precio: 300 }
];

// "Traeme todos MENOS los audifonos"
const resultado = carrito.filter(producto => producto.nombre !== 'Audifonos');

console.log(resultado);
console.log('El carrito original sigue completo:', carrito.length);

✓ Por que se usa tanto

En la Unidad 8 eliminabas con splice, que modifica el arreglo original y exige conocer la posicion. Con filter solo describes que quieres conservar y obtienes un arreglo nuevo. Es la forma habitual de eliminar elementos en aplicaciones modernas.

12.6 find: traer el elemento, no su posicion

find frente a findIndex
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 700 }
];

const indice = carrito.findIndex(producto => producto.nombre === 'Tablet');
const objeto = carrito.find(producto => producto.nombre === 'Tablet');

console.log(indice);   // 1              -> la POSICION
console.log(objeto);   // { nombre... }  -> el OBJETO completo
Si no encuentra nada
const carrito = [{ nombre: 'Tablet', precio: 100 }];

console.log(carrito.find(p => p.nombre === 'Monitor'));        // undefined
console.log(carrito.findIndex(p => p.nombre === 'Monitor'));   // -1

⚠ find tambien devuelve solo el primero

Si dos productos cumplen la condicion, find se queda con el primero. Para obtener todos, usa filter.

12.7 every: que lo cumplan todos

some devuelve true si al menos uno cumple la condicion. every exige que la cumplan todos.

every frente a some
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 700 }
];

// TODOS cuestan menos de 1000?
console.log(carrito.every(producto => producto.precio < 1000));   // true

// TODOS cuestan menos de 500?
console.log(carrito.every(producto => producto.precio < 500));    // false

// ALGUNO cuesta menos de 500?
console.log(carrito.some(producto => producto.precio < 500));     // true

💡 La analogia que lo fija

every es el && de los arreglos (todas las condiciones) y some es el || (basta una). Los operadores logicos de la Unidad 10, aplicados a una coleccion entera.

12.8 Unir arreglos: concat y spread

Con .concat()
const meses  = ['Enero', 'Febrero', 'Marzo'];
const meses2 = ['Abril', 'Mayo', 'Junio'];
const meses3 = ['Julio', 'Agosto', 'Septiembre'];

const resultado = meses.concat(meses2, meses3);
console.log(resultado);
Con el spread operator
const meses  = ['Enero', 'Febrero', 'Marzo'];
const meses2 = ['Abril', 'Mayo', 'Junio'];
const meses3 = ['Julio', 'Agosto', 'Septiembre'];

const resultado2 = [...meses, ...meses2, ...meses3];
console.log(resultado2);

// El orden determina el resultado
const alReves = [...meses3, ...meses];
console.log(alReves);

💡 Caso real donde el orden importa

En un gestor de tareas tipo Trello o Asana, las tareas nuevas suelen aparecer arriba. Ese comportamiento se consigue escribiendo [nuevaTarea, ...tareas], poniendo lo nuevo delante de la copia del arreglo.

12.9 Mas sobre el spread operator

Anadir un elemento sin modificar el original
const meses = ['Enero', 'Febrero', 'Marzo'];

const meses2 = [...meses, 'Abril'];

console.log(meses);    // ['Enero', 'Febrero', 'Marzo']  <-- intacto
console.log(meses2);   // con Abril al final
Compara con push, que si lo modifica
const meses = ['Enero', 'Febrero', 'Marzo'];

meses.push('Abril');

console.log(meses);   // el original ha cambiado

💡 Programacion funcional

Uno de los principios de la programacion funcional es no modificar los datos originales, sino crear versiones nuevas. El spread operator es la herramienta principal para lograrlo, y por eso es tan habitual en JavaScript moderno y en React.

Dos errores tipicos

Error 1: usar spread sobre un string
const meses = ['Enero', 'Febrero'];

// MAL: los tres puntos descomponen el texto letra por letra
console.log([...meses, ...'Marzo']);

// BIEN: un string se anade tal cual, sin los tres puntos
console.log([...meses, 'Marzo']);
Error 2: usar spread sobre un objeto dentro de un arreglo
const carrito = [{ nombre: 'Monitor', precio: 400 }];
const producto = { nombre: 'Disco duro', precio: 300 };

// BIEN: el objeto se anade entero, sin los tres puntos
const carrito2 = [...carrito, producto];
console.log(carrito2);

// Si pusieras [...carrito, ...producto] obtendrias:
// TypeError: producto is not iterable

⚠ La regla

Los tres puntos van delante de lo que quieres descomponer (otro arreglo). Lo que quieres anadir como un unico elemento va sin ellos.

12.10 Chuleta: ¿cual necesito?

Quiero saber / hacer...MetodoDevuelve
¿Existe este valor? (arreglo plano)includes()true / false
¿Existe? (tambien con objetos)some()true / false
¿Lo cumplen todos?every()true / false
¿En que posicion esta?findIndex()Numero, o -1
Traeme el elementofind()El elemento, o undefined
Traeme todos los que cumplanfilter()Arreglo nuevo
Transformar cada elementomap()Arreglo nuevo
Hacer algo con cada elementoforEach()undefined
Reducirlo todo a un valorreduce()Un unico valor
Unir arreglosconcat() o [...a, ...b]Arreglo nuevo

💡 Los dos que mas usaras

filter y reduce. El primero para seleccionar, el segundo para totalizar. Aparecen en practicamente todos los proyectos del curso.

12.11 Ejercicios practicos

Ejercicio 1

Resumen del carrito

Con el carrito de la seccion 12.1, calcula y muestra: cuantos productos hay, el total a pagar, si hay algun producto que cueste mas de 600, y si todos cuestan mas de 50.

Ver solucion
Solucion
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Audifonos', precio: 300 },
    { nombre: 'Teclado', precio: 400 },
    { nombre: 'Celular', precio: 700 }
];

console.log('Productos:', carrito.length);
console.log('Total:', carrito.reduce((total, p) => total + p.precio, 0));
console.log('Alguno > 600?', carrito.some(p => p.precio > 600));
console.log('Todos > 50?', carrito.every(p => p.precio > 50));
Ejercicio 2

Eliminar del carrito

La persona pulsa el boton de eliminar sobre el Teclado. Quitalo del carrito con filter y recalcula el nuevo total.

Ver solucion
Solucion
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Teclado', precio: 400 }
];

const totalAntes = carrito.reduce((total, p) => total + p.precio, 0);
console.log('Total antes:', totalAntes);

const nuevoCarrito = carrito.filter(p => p.nombre !== 'Teclado');

const totalDespues = nuevoCarrito.reduce((total, p) => total + p.precio, 0);
console.log('Productos restantes:', nuevoCarrito.length);
console.log('Total despues:', totalDespues);
Reto 1

Un buscador con filtros

Con un arreglo de coches (marca, color, precio, puertas), escribe un buscador que devuelva los que sean de una marca concreta y cuesten menos de un maximo. Es la base del proyecto de buscador de autos del curso.

Ver solucion
Solucion
const autos = [
    { marca: 'Ford',  color: 'negro',  precio: 65000,  puertas: 4 },
    { marca: 'BMW',   color: 'blanco', precio: 90000,  puertas: 2 },
    { marca: 'Ford',  color: 'rojo',   precio: 72000,  puertas: 4 },
    { marca: 'BMW',   color: 'negro',  precio: 120000, puertas: 4 },
    { marca: 'Audi',  color: 'blanco', precio: 85000,  puertas: 2 }
];

function buscar(marca, precioMaximo) {
    const resultado = autos
        .filter(auto => auto.marca === marca)
        .filter(auto => auto.precio <= precioMaximo);

    if (resultado.length === 0) {
        console.log('No hay resultados');
        return;
    }

    console.log(`${resultado.length} resultado(s):`);
    resultado.forEach(auto => console.log(`  ${auto.marca} ${auto.color} - ${auto.precio}`));
}

buscar('Ford', 80000);
buscar('BMW', 100000);
buscar('Audi', 50000);

💡 Fijate en el encadenamiento

Como filter devuelve un arreglo, puedes aplicarle otro filter directamente. Asi se anaden tantos criterios de busqueda como quieras sin complicar el codigo.

Reto 2

Ticket completo en una cadena

A partir del carrito, genera un ticket que muestre cada linea como "Producto x1 .... 500" y al final el total con el 16% de impuesto, combinando map, forEach y reduce.

Ver solucion
Solucion
const carrito = [
    { nombre: 'Monitor 27 pulgadas', precio: 500 },
    { nombre: 'Tablet', precio: 100 },
    { nombre: 'Celular', precio: 700 }
];

// 1. map transforma cada objeto en una linea de texto
const lineas = carrito.map(p => `${p.nombre} x1 .... ${p.precio}`);

// 2. forEach las imprime
console.log('--- TICKET ---');
lineas.forEach(linea => console.log(linea));

// 3. reduce calcula el subtotal
const subtotal = carrito.reduce((total, p) => total + p.precio, 0);

console.log('--------------');
console.log(`Subtotal: ${subtotal}`);
console.log(`IVA 16%:  ${(subtotal * 0.16).toFixed(2)}`);
console.log(`TOTAL:    ${(subtotal * 1.16).toFixed(2)}`);

Cada metodo hace una sola cosa: map transforma, forEach muestra y reduce acumula. Elegir el metodo adecuado para cada tarea es justo lo que separa el codigo legible del codigo enredado.

12.12 Resumen de lo aprendido

  • Los array methods sustituyen a los bucles manuales y reducen el codigo a una linea.
  • Todos reciben una arrow function a la que se le pasa cada elemento automaticamente.
  • includes() comprueba si existe un valor (solo arreglos planos); some() hace lo mismo y ademas sirve con objetos.
  • every() exige que todos cumplan la condicion; some() se conforma con uno.
  • findIndex() devuelve la posicion (o -1); find() devuelve el elemento (o undefined). Ambos solo el primero.
  • filter() devuelve un arreglo nuevo con todos los que cumplen. Es la forma moderna de eliminar elementos.
  • reduce() acumula todo en un valor. Su primer parametro es el acumulador y el segundo el elemento; el valor inicial va despues de la funcion.
  • Unir arreglos: concat() o [...a, ...b]. El orden determina el resultado.
  • El spread operator no modifica el original, a diferencia de push. Es la base de la programacion funcional.

12.13 Verificacion de cierre

¿Cual de estos no funciona con un arreglo de objetos?

  • some()
  • filter()
  • findIndex()
  • includes()

En carrito.reduce((total, producto) => ...), ¿que es total?

  • El numero de elementos del arreglo
  • El acumulador: el valor arrastrado desde la vuelta anterior
  • El elemento actual del arreglo
  • El indice de la vuelta actual

Quieres todos los productos que cuesten mas de 400. ¿Que metodo usas?

  • filter()
  • find()
  • findIndex()
  • some()

🏁 Unidad completada