Saltar al contenido
schedule 15 min JavaScript

Arrays y objetos

Hasta ahora has guardado datos sueltos en variables: un nombre, un número, un booleano. Pero en el mundo real trabajas con listas de cosas (productos de un carrito, episodios de una serie, usuarios de una app) y con entidades que tienen varias propiedades (un usuario con nombre, email y rol). Para eso existen los arrays y los objetos. En la sección de Lógica ya trabajaste con listas en pseudocódigo (buscar, filtrar, ordenar) — ahora vas a ver cómo JavaScript te da herramientas aún más potentes. Esta lección es probablemente la más importante de la sección de JavaScript.

Arrays: listas ordenadas

Un array es una lista ordenada de elementos. Cada elemento tiene un índice que empieza en 0:

arrays.js
// Crear un array
const generos = ["Acción", "Comedia", "Drama", "Sci-Fi", "Terror"];

// Acceder por índice (empiezan en 0)
console.log(generos[0]); // "Acción"
console.log(generos[3]); // "Sci-Fi"

// Longitud del array
console.log(generos.length); // 5

// Último elemento (truco clásico)
console.log(generos[generos.length - 1]); // "Terror"

// Último elemento (forma moderna)
console.log(generos.at(-1)); // "Terror"
console.log(generos.at(-2)); // "Sci-Fi"

Modificar arrays: push, pop, shift, unshift

arrays.js
const playlist = ["Bohemian Rhapsody", "Stairway to Heaven"];

// push: añadir al final
playlist.push("Hotel California");
console.log(playlist);
// ["Bohemian Rhapsody", "Stairway to Heaven", "Hotel California"]

// pop: quitar el último
const eliminada = playlist.pop();
console.log(eliminada); // "Hotel California"
console.log(playlist);  // ["Bohemian Rhapsody", "Stairway to Heaven"]

// unshift: añadir al principio
playlist.unshift("Imagine");
console.log(playlist);
// ["Imagine", "Bohemian Rhapsody", "Stairway to Heaven"]

// shift: quitar el primero
playlist.shift();
console.log(playlist); // ["Bohemian Rhapsody", "Stairway to Heaven"]

// includes: comprobar si existe un elemento
console.log(playlist.includes("Imagine")); // false
console.log(playlist.includes("Bohemian Rhapsody")); // true

Recorrer arrays: forEach

forEach ejecuta una función por cada elemento del array. Ideal cuando quieres hacer algo con cada elemento sin necesitar un resultado nuevo:

arrays.js
const tareas = ["Comprar leche", "Estudiar JavaScript", "Llamar al dentista"];

tareas.forEach((tarea, indice) => {
    console.log(`${indice + 1}. ${tarea}`);
});
// 1. Comprar leche
// 2. Estudiar JavaScript
// 3. Llamar al dentista

map: transformar cada elemento

map es como forEach, pero devuelve un nuevo array con los resultados. No modifica el original:

arrays.js
const precios = [10, 25, 50, 100];

// Aplicar un 21% de IVA a cada precio
const preciosConIVA = precios.map(precio => precio * 1.21);
console.log(preciosConIVA); // [12.1, 30.25, 60.5, 121]

// El array original no cambia
console.log(precios); // [10, 25, 50, 100]

// Otro ejemplo: formatear nombres
const nombres = ["ana garcía", "carlos lópez", "maría ruiz"];
const formateados = nombres.map(nombre => {
    return nombre
        .split(" ")
        .map(palabra => palabra.charAt(0).toUpperCase() + palabra.slice(1))
        .join(" ");
});
console.log(formateados); // ["Ana García", "Carlos López", "María Ruiz"]

filter: quedarte solo con lo que necesitas

arrays.js
const productos = [
    { nombre: "Camiseta", precio: 25, enStock: true },
    { nombre: "Pantalón", precio: 45, enStock: false },
    { nombre: "Zapatillas", precio: 89, enStock: true },
    { nombre: "Gorra", precio: 15, enStock: true },
    { nombre: "Chaqueta", precio: 120, enStock: false },
];

// Productos disponibles
const disponibles = productos.filter(producto => producto.enStock);
console.log(disponibles.length); // 3

// Productos baratos (menos de 50€)
const baratos = productos.filter(producto => producto.precio < 50);
console.log(baratos);
// [{ nombre: "Camiseta"... }, { nombre: "Pantalón"... }, { nombre: "Gorra"... }]

// Combinar: disponibles Y baratos
const oferta = productos.filter(p => p.enStock && p.precio < 50);
console.log(oferta);
// [{ nombre: "Camiseta"... }, { nombre: "Gorra"... }]

find y some/every

arrays.js
const usuarios = [
    { id: 1, nombre: "Ana", activo: true },
    { id: 2, nombre: "Luis", activo: false },
    { id: 3, nombre: "Sara", activo: true },
];

// find: devuelve el PRIMER elemento que cumple la condición
const ana = usuarios.find(u => u.nombre === "Ana");
console.log(ana); // { id: 1, nombre: "Ana", activo: true }

// findIndex: igual, pero devuelve el índice
const indiceLuis = usuarios.findIndex(u => u.nombre === "Luis");
console.log(indiceLuis); // 1

// some: ¿alguno cumple la condición? (devuelve true/false)
const hayInactivos = usuarios.some(u => !u.activo);
console.log(hayInactivos); // true

// every: ¿TODOS cumplen la condición?
const todosActivos = usuarios.every(u => u.activo);
console.log(todosActivos); // false

reduce: el método más potente

reduce recorre el array acumulando un resultado. Es perfecto para calcular totales, agrupar datos o transformar un array en cualquier otra estructura:

carrito.js
const carrito = [
    { nombre: "Teclado mecánico", precio: 89, cantidad: 1 },
    { nombre: "Ratón gaming", precio: 45, cantidad: 1 },
    { nombre: "Monitor 27\"", precio: 299, cantidad: 2 },
    { nombre: "Cable USB-C", precio: 12, cantidad: 3 },
];

// Calcular el total del carrito
const total = carrito.reduce((acumulador, producto) => {
    return acumulador + (producto.precio * producto.cantidad);
}, 0); // 0 es el valor inicial del acumulador

console.log(total); // 534

// Contar el total de artículos
const totalArticulos = carrito.reduce((acc, producto) => {
    return acc + producto.cantidad;
}, 0);

console.log(totalArticulos); // 7

// Resumen con reduce + template literal
const resumen = carrito.reduce((acc, p) => {
    return acc + `  - ${p.nombre} x${p.cantidad}: ${p.precio * p.cantidad}€\n`;
}, "🛒 Tu carrito:\n");

console.log(resumen);
// 🛒 Tu carrito:
//   - Teclado mecánico x1: 89€
//   - Ratón gaming x1: 45€
//   - Monitor 27" x2: 598€
//   - Cable USB-C x3: 36€

Objetos: entidades con propiedades

Un objeto agrupa datos relacionados bajo un mismo nombre. Cada dato tiene una clave (key) y un valor:

objetos.js
const pelicula = {
    titulo: "Interstellar",
    director: "Christopher Nolan",
    año: 2014,
    generos: ["Sci-Fi", "Drama", "Aventura"],
    puntuacion: 8.7,
    enCartelera: false,
};

// Acceso con punto (lo más común)
console.log(pelicula.titulo);     // "Interstellar"
console.log(pelicula.generos[0]); // "Sci-Fi"

// Acceso con corchetes (necesario cuando la clave es dinámica o tiene caracteres especiales)
console.log(pelicula["director"]); // "Christopher Nolan"

const campo = "puntuacion";
console.log(pelicula[campo]); // 8.7 — útil cuando no sabes la clave de antemano

// Modificar propiedades
pelicula.puntuacion = 8.8;

// Añadir propiedades nuevas
pelicula.duracion = 169;

// Eliminar propiedades
delete pelicula.enCartelera;

Objetos anidados

objetos.js
const usuario = {
    nombre: "Elena",
    perfil: {
        avatar: "elena.jpg",
        bio: "Desarrolladora frontend",
        redes: {
            github: "elena-dev",
            linkedin: "elena-developer",
        },
    },
    cursos: ["JavaScript", "React", "Node.js"],
};

// Acceder a datos anidados
console.log(usuario.perfil.redes.github); // "elena-dev"
console.log(usuario.cursos[1]);           // "React"

// Optional chaining (?.) — evita errores si una propiedad no existe
console.log(usuario.perfil.redes.twitter);   // undefined
console.log(usuario.trabajo?.empresa);       // undefined (sin error)
// Sin ?. esto daría error: Cannot read property "empresa" of undefined

Spread operator: copiar y combinar

El spread ... "desparrama" los elementos de un array u objeto. Es imprescindible para trabajar de forma inmutable (sin modificar los originales):

spread.js
// ARRAYS: copiar y combinar
const frontend = ["HTML", "CSS", "JavaScript"];
const backend = ["Node.js", "Python", "PHP"];

// Combinar arrays
const fullstack = [...frontend, ...backend];
console.log(fullstack);
// ["HTML", "CSS", "JavaScript", "Node.js", "Python", "PHP"]

// Copiar un array (copia superficial)
const copiaFrontend = [...frontend];
copiaFrontend.push("TypeScript");
console.log(frontend);      // ["HTML", "CSS", "JavaScript"] — no se modifica
console.log(copiaFrontend); // ["HTML", "CSS", "JavaScript", "TypeScript"]

// OBJETOS: copiar y combinar
const configBase = { tema: "oscuro", idioma: "es", fontSize: 16 };
const configUsuario = { fontSize: 18, sidebar: true };

// Combinar objetos (las últimas propiedades ganan)
const config = { ...configBase, ...configUsuario };
console.log(config);
// { tema: "oscuro", idioma: "es", fontSize: 18, sidebar: true }

// Copiar y modificar (patrón muy común)
const configNueva = { ...config, tema: "claro" };
console.log(configNueva.tema); // "claro"
console.log(config.tema);      // "oscuro" — el original intacto

Destructuring: extraer valores rápido

destructuring.js
// DESTRUCTURING DE OBJETOS
const serie = {
    titulo: "Breaking Bad",
    temporadas: 5,
    nota: 9.5,
    plataforma: "Netflix",
};

// En vez de: const titulo = serie.titulo; const nota = serie.nota;
const { titulo, nota, plataforma } = serie;
console.log(titulo); // "Breaking Bad"
console.log(nota);   // 9.5

// Renombrar variables
const { titulo: nombreSerie, temporadas: numTemporadas } = serie;
console.log(nombreSerie);    // "Breaking Bad"
console.log(numTemporadas);  // 5

// Valores por defecto
const { genero = "Drama" } = serie;
console.log(genero); // "Drama" (no existía en el objeto)

// DESTRUCTURING DE ARRAYS
const coordenadas = [40.4168, -3.7038]; // Madrid
const [latitud, longitud] = coordenadas;
console.log(latitud);  // 40.4168
console.log(longitud); // -3.7038

// Saltar elementos
const podio = ["Oro", "Plata", "Bronce"];
const [, plata] = podio;
console.log(plata); // "Plata"

// Destructuring en parámetros de función — MUY útil
const mostrarPelicula = ({ titulo, director, año }) => {
    console.log(`${titulo} (${año}) — Dir: ${director}`);
};

mostrarPelicula({ titulo: "Dune", director: "Denis Villeneuve", año: 2021 });
// "Dune (2021) — Dir: Denis Villeneuve"

structuredClone: copia profunda de verdad

El spread ... solo hace una copia superficial: si el objeto tiene objetos anidados, esos siguen apuntando al mismo sitio. structuredClone() resuelve eso de forma limpia:

clone.js
const original = {
    nombre: "Equipo A",
    miembros: ["Ana", "Luis", "Sara"],
    config: { tema: "oscuro", notificaciones: true },
};

// Copia superficial con spread — PROBLEMA
const copiaSuperficial = { ...original };
copiaSuperficial.miembros.push("Pedro");
console.log(original.miembros); // ["Ana", "Luis", "Sara", "Pedro"] — ¡modificaste el original!

// Copia profunda con structuredClone — SOLUCIÓN
const copiaProfunda = structuredClone(original);
copiaProfunda.miembros.push("María");
copiaProfunda.config.tema = "claro";

console.log(original.miembros);    // ["Ana", "Luis", "Sara", "Pedro"] — intacto
console.log(original.config.tema); // "oscuro" — intacto

// Antes se usaba JSON.parse(JSON.stringify(obj)), pero eso falla con
// Dates, Maps, Sets, undefined, funciones... structuredClone es la forma correcta.

Object.groupBy: agrupar de forma nativa

Object.groupBy() es una adición moderna a JavaScript que te permite agrupar elementos de un array por una clave. Antes tenías que hacerlo manualmente con reduce:

groupby.js
const pedidos = [
    { id: 1, producto: "Camiseta", estado: "enviado" },
    { id: 2, producto: "Pantalón", estado: "pendiente" },
    { id: 3, producto: "Zapatillas", estado: "enviado" },
    { id: 4, producto: "Gorra", estado: "entregado" },
    { id: 5, producto: "Chaqueta", estado: "pendiente" },
    { id: 6, producto: "Bufanda", estado: "entregado" },
];

// Agrupar por estado
const porEstado = Object.groupBy(pedidos, pedido => pedido.estado);

console.log(porEstado);
// {
//   enviado: [{ id: 1, ... }, { id: 3, ... }],
//   pendiente: [{ id: 2, ... }, { id: 5, ... }],
//   entregado: [{ id: 4, ... }, { id: 6, ... }],
// }

console.log(porEstado.pendiente.length); // 2

// Otro ejemplo: agrupar por rango de precio
const productos = [
    { nombre: "Cable", precio: 8 },
    { nombre: "Funda", precio: 15 },
    { nombre: "Teclado", precio: 89 },
    { nombre: "Monitor", precio: 299 },
    { nombre: "Ratón", precio: 45 },
];

const porRango = Object.groupBy(productos, p => {
    if (p.precio < 20) return "económico";
    if (p.precio < 100) return "medio";
    return "premium";
});

console.log(porRango.económico); // [{ nombre: "Cable"... }, { nombre: "Funda"... }]
console.log(porRango.premium);   // [{ nombre: "Monitor"... }]

Ejemplo completo: tracker de series

Vamos a combinar todo lo aprendido en un ejemplo real. Un tracker donde gestionas las series que estás viendo:

series-tracker.js
const series = [
    { titulo: "Breaking Bad", temporada: 5, episodio: 16, viendo: false, nota: 9.5 },
    { titulo: "The Bear", temporada: 2, episodio: 5, viendo: true, nota: 8.7 },
    { titulo: "Severance", temporada: 2, episodio: 3, viendo: true, nota: 8.9 },
    { titulo: "The Last of Us", temporada: 1, episodio: 9, viendo: false, nota: 8.8 },
    { titulo: "Succession", temporada: 4, episodio: 10, viendo: false, nota: 8.9 },
    { titulo: "Shogun", temporada: 1, episodio: 7, viendo: true, nota: 8.6 },
];

// ¿Qué estoy viendo ahora?
const enProgreso = series.filter(s => s.viendo);
console.log(`Viendo ${enProgreso.length} series`);

// Títulos de las que estoy viendo
const titulosActivos = enProgreso.map(s => s.titulo);
console.log(titulosActivos); // ["The Bear", "Severance", "Shogun"]

// ¿Alguna tiene nota mayor a 9?
const hayObrasMaestras = series.some(s => s.nota >= 9);
console.log(hayObrasMaestras); // true (Breaking Bad)

// Nota media de todas las series
const notaMedia = series.reduce((acc, s) => acc + s.nota, 0) / series.length;
console.log(`Nota media: ${notaMedia.toFixed(1)}`); // "Nota media: 8.9"

// Añadir una serie nueva (sin mutar el original)
const seriesActualizadas = [
    ...series,
    { titulo: "Fallout", temporada: 1, episodio: 1, viendo: true, nota: null },
];

// Marcar una serie como terminada (sin mutar)
const terminarSerie = (lista, titulo) => {
    return lista.map(s =>
        s.titulo === titulo ? { ...s, viendo: false } : s
    );
};

const despuesDeShogun = terminarSerie(seriesActualizadas, "Shogun");
console.log(despuesDeShogun.find(s => s.titulo === "Shogun").viendo); // false

// Agrupar por estado
const porEstado = Object.groupBy(seriesActualizadas, s =>
    s.viendo ? "En progreso" : "Completadas"
);
console.log(porEstado["En progreso"].length);  // 4
console.log(porEstado["Completadas"].length);  // 3

Ejemplo completo: operaciones de carrito de compra

carrito.js
// Estado inicial del carrito
let carrito = [
    { id: 1, nombre: "Teclado mecánico", precio: 89, cantidad: 1 },
    { id: 2, nombre: "Monitor 27\"", precio: 299, cantidad: 1 },
];

// Añadir producto (si ya existe, incrementar cantidad)
const añadirProducto = (carrito, producto) => {
    const existente = carrito.find(item => item.id === producto.id);

    if (existente) {
        return carrito.map(item =>
            item.id === producto.id
                ? { ...item, cantidad: item.cantidad + 1 }
                : item
        );
    }

    return [...carrito, { ...producto, cantidad: 1 }];
};

// Eliminar producto
const eliminarProducto = (carrito, idProducto) => {
    return carrito.filter(item => item.id !== idProducto);
};

// Calcular total
const calcularTotal = (carrito) => {
    return carrito.reduce((total, item) => total + item.precio * item.cantidad, 0);
};

// Número total de artículos
const contarArticulos = (carrito) => {
    return carrito.reduce((total, item) => total + item.cantidad, 0);
};

// Usar las funciones
carrito = añadirProducto(carrito, { id: 3, nombre: "Ratón gaming", precio: 45 });
carrito = añadirProducto(carrito, { id: 1, nombre: "Teclado mecánico", precio: 89 }); // +1

console.log(carrito);
// [
//   { id: 1, nombre: "Teclado mecánico", precio: 89, cantidad: 2 },
//   { id: 2, nombre: "Monitor 27\"", precio: 299, cantidad: 1 },
//   { id: 3, nombre: "Ratón gaming", precio: 45, cantidad: 1 },
// ]

console.log(`Total: ${calcularTotal(carrito)}€`);    // "Total: 522€"
console.log(`Artículos: ${contarArticulos(carrito)}`); // "Artículos: 4"

carrito = eliminarProducto(carrito, 2);
console.log(`Total sin monitor: ${calcularTotal(carrito)}€`); // "Total sin monitor: 223€"
code

Gestiona tu playlist de música

Medio schedule 20 min

Crea un archivo playlist.js con un array de al menos 8 canciones. Cada canción es un objeto con: titulo, artista, genero, duracionSegundos y favorita (boolean).

  • Usa filter para obtener solo las canciones favoritas.
  • Usa map para crear un array con el formato "Artista - Título".
  • Usa find para buscar una canción por título.
  • Usa reduce para calcular la duración total de la playlist en minutos.
  • Usa some para comprobar si hay alguna canción de un género concreto.
  • Usa every para comprobar si todas las canciones duran menos de 5 minutos.
  • Usa Object.groupBy para agrupar las canciones por género.
  • Añade una canción nueva con spread (...) sin mutar el array original.
  • Usa destructuring para extraer el título y artista de la primera canción.
lightbulb Pistas

Para la duración total: const totalSegundos = canciones.reduce((acc, c) => acc + c.duracionSegundos, 0), luego divide entre 60 y usa toFixed(1). Para Object.groupBy: Object.groupBy(canciones, c => c.genero). Para destructuring del primer elemento: const [{ titulo, artista }] = canciones;.

Newsletter

Recibe nuevos cursos, actualizaciones, artículos del blog y promociones en tu correo.