Tres horas. Media hora para entender qué devuelve cada método de array y cómo se modela una entidad con un objeto, y dos horas y media convirtiendo tus listas paralelas en el catálogo que usarás el resto del módulo.
Hoy · Hoja de ruta
- 1. Aprende: Cómo se guarda una lista, qué métodos la modifican y cuáles devuelven otra, qué devuelve exactamente
map,filter,findyreduce, y qué es JSON. - 2. Haz: Construye tu catálogo como array de objetos, consúltalo con métodos declarativos y conviértelo a JSON y de vuelta.
- 3. Comprueba: Eliges el método por lo que devuelve, y el catálogo original nunca queda alterado sin que lo hayas querido.
Antes de empezar · 5 minutos, sin apuntes
- En la sesión 2 usaste tres listas paralelas. ¿Qué problema tiene eso?
- ¿Qué pasa si pides el elemento número cien de una lista de tres?
- En la sesión 3 escribiste
aplicarATodosyfiltrarPor. ¿Qué devolvía cada una?
Se explica
25 minutos · conceptos y demostración
Hoy hay dos preguntas que se repiten en cada línea: ¿esto modifica el original o devuelve algo nuevo? y ¿qué me devuelve exactamente este método?. Casi todos los errores de la sesión son una de las dos mal contestada.
Una lista ordenada de valores
const categorias = ["teclados", "ratones", "monitores"];
categorias[0] // "teclados" ← se empieza a contar en cero
categorias.length // 3
categorias[99] // undefined ← no da error
categorias.at(-1) // "monitores" ← el último, sin calcular índices
Que pedir un índice inexistente devuelva undefined en lugar de fallar es cómodo y peligroso a partes iguales: el programa sigue, y el undefined viaja hasta reventar tres funciones más allá.
Distingue mutar de devolver
Algunos métodos cambian el array original —push, pop, splice, sort, reverse— y otros devuelven uno nuevo sin tocarlo: slice, concat, map, filter.
El error clásico: ordenar «una copia» que en realidad era el mismo array, y descubrir después que el catálogo original quedó desordenado para todo el programa. Ante la duda, copia primero.
const original = [3, 1, 2];
const ordenado = [...original].sort(); // copia y ordena la copia
original; // [3, 1, 2], intacto
Los tres puntos, el operador de propagación, copian los elementos en un array nuevo.
Cada método devuelve una cosa distinta
| Método | Devuelve | Pregunta que responde |
|---|---|---|
map |
Un array del mismo tamaño | «Transforma cada uno» |
filter |
Un array más corto | «Quédate con los que cumplan» |
find |
Un elemento, o undefined |
«Dame el primero que cumpla» |
findIndex |
Una posición, o -1 |
«¿En qué posición está?» |
some |
true o false |
«¿Hay alguno que cumpla?» |
every |
true o false |
«¿Cumplen todos?» |
sort |
El array ordenado | «Ponlos en este orden» |
reduce |
Un solo valor | «Combínalos todos en uno» |
forEach |
Nada | «Haz algo con cada uno» |
Elige por lo que necesitas recibir
La mitad de los errores con estos métodos son de elección, no de escritura: usar filter y quedarse con [0] cuando lo que se quería era find, o usar find esperando una lista.
Pregúntate qué quieres tener después: ¿otra lista, un elemento, un sí o un no, o un número?
const productos = [
{ nombre: "Teclado", precio: 89.9, stock: 4 },
{ nombre: "Ratón", precio: 24.5, stock: 0 },
{ nombre: "Monitor", precio: 199, stock: 7 }
];
const nombres = productos.map((p) => p.nombre);
const disponibles = productos.filter((p) => p.stock > 0);
const caro = productos.find((p) => p.precio > 100);
const hayAgotados = productos.some((p) => p.stock === 0);
const todosBaratos = productos.every((p) => p.precio < 500);
sort y su trampa
[10, 9, 100].sort(); // [10, 100, 9] ← compara como texto
[10, 9, 100].sort((a, b) => a - b); // [9, 10, 100]
Sin función de comparación, sort convierte a texto. Con números hay que pasarla siempre: a - b ascendente, b - a descendente. Para textos con acentos y mayúsculas:
productos.sort((a, b) => a.nombre.localeCompare(b.nombre));
Recuerda además que sort muta: ordena sobre una copia si el orden original importa.
reduce es el que da más miedo y el que menos lo merece:
const total = productos.reduce((suma, p) => suma + p.precio * p.stock, 0);
Se lee así: empieza con 0, y por cada producto, quédate con lo que llevabas más lo que aporta este. Es el acumulador de la sesión 2, escrito en una línea.
Como cada método devuelve un array, el siguiente puede trabajar sobre él:
const resumen = productos
.filter((p) => p.stock > 0)
.sort((a, b) => a.precio - b.precio)
.map((p) => `${p.nombre}: ${p.precio.toFixed(2)} €`);
Se lee de arriba abajo como una frase: filtra, ordena, formatea. Con sort en medio conviene fijarse: aquí es seguro porque filter ya devolvió un array nuevo.
Un objeto agrupa datos con nombre
const producto = {
id: 1,
nombre: "Teclado mecánico",
precio: 89.9,
categoria: "teclados",
stock: 4,
activo: true
};
producto.nombre // "Teclado mecánico"
producto["precio"] // 89.9, útil cuando la clave es una variable
producto.color // undefined
Un catálogo, por tanto, es un array de objetos:
const catalogo = [
{ id: 1, nombre: "Teclado", precio: 89.9, stock: 4 },
{ id: 2, nombre: "Ratón", precio: 24.5, stock: 0 }
];
Esta es la estructura que vas a usar durante el resto del módulo: en la UD4 la pintarás en la página, en la UD5 la guardarás en un fichero y en la UD6 la servirá tu API.
Una const con un objeto sí se puede modificar por dentro
const protege la asignación, no el contenido. Puedes cambiar las propiedades de un objeto declarado con const; lo que no puedes es apuntarlo a otro objeto distinto.
De ahí sale otro clásico: dos variables que apuntan al mismo objeto. Modificar una cambia «las dos», porque siempre fue una. Para copiar de verdad: { ...producto }.
Al acceder a datos anidados aparece el fallo más habitual con datos ajenos, y su remedio:
const pedido = {
id: 1001,
cliente: { nombre: "Ana", email: "ana@ejemplo.com" },
lineas: [{ producto: "Teclado", unidades: 1 }]
};
pedido.cliente.nombre // "Ana"
pedido.lineas[0].unidades // 1
pedido.envio.direccion // TypeError: no se puede leer de undefined
pedido.envio?.direccion // undefined, sin romper nada
El interrogante es el encadenamiento opcional: si lo de la izquierda no existe, devuelve undefined en lugar de lanzar un error.
Para sacar las propiedades que necesitas y darles nombre está la desestructuración, que ahorra repetir producto. diez veces y hace explícito qué usa cada función:
const { nombre, precio } = producto;
const [primero, segundo] = catalogo;
function describir({ nombre, precio }) {
return `${nombre} · ${precio.toFixed(2)} €`;
}
JSON, el formato de intercambio
JSON
Un formato de texto para representar datos, nacido de la sintaxis de los objetos de JavaScript pero independiente de él: lo entienden Java, Python, PHP y cualquier otro lenguaje. Es como viajan los datos entre un navegador y un servidor.
const texto = JSON.stringify(producto); // objeto → texto
const texto2 = JSON.stringify(producto, null, 2); // con sangría, legible
const recuperado = JSON.parse(texto); // texto → objeto
Sus reglas son más estrictas que las de JavaScript:
{
"nombre": "Teclado mecánico",
"precio": 89.9,
"stock": 4,
"activo": true,
"etiquetas": ["mecánico", "retroiluminado"]
}
Las claves van siempre entre comillas dobles, no se admiten comillas simples, ni comas finales, ni comentarios, ni funciones, ni undefined. Un JSON.parse de un texto mal formado lanza un error: en la sesión 5 lo trataremos como se debe.
Se trabaja
150 minutos · práctica sobre tu propio proyecto
Hoy el catálogo deja de ser un conjunto de listas sueltas y pasa a ser la estructura de datos del resto del módulo. El último paso cierra el círculo abierto en la sesión 3.
Paso 1 · El catálogo como lista · 30 min
Crea js/datos.js:
- Declara un array con al menos ocho nombres de producto de tu tema.
- Otro con sus precios, en el mismo orden.
- Escribe funciones para añadir, quitar y buscar un producto.
- Ordena una copia por precio sin alterar el original, y demuéstralo imprimiendo los dos.
- Escribe una función que devuelva los tres primeros.
- Escribe una función que devuelva el array sin duplicados.
Consultar · añadir, quitar y buscar
const lista = ["teclados", "ratones"];
lista.push("monitores"); // añade al final → 3
lista.pop(); // quita el último → "monitores"
lista.unshift("cables"); // añade al principio
lista.shift(); // quita el primero
lista.includes("ratones"); // true
lista.indexOf("ratones"); // 1, o -1 si no está
Para recorrer haciendo algo con cada elemento, sin obtener otra lista:
categorias.forEach((categoria, indice) => {
console.log(`${indice + 1}. ${categoria}`);
});
Antes de continuar: después de ordenar la copia, el array original imprime exactamente el mismo orden que tenía al declararlo.
Paso 2 · Consultas del catálogo · 40 min
En js/catalogo.js, sobre tu lista de productos:
nombresDisponibles(productos)confilterymap.buscarPorNombre(productos, texto)confind, sin distinguir mayúsculas.hayAgotados(productos)consome.valorAlmacen(productos)conreduce.ordenarPorPrecio(productos, ascendente)que no mute el original.- Una consulta encadenada que responda a una pregunta compuesta de tu tema.
Para cada función, escribe en un comentario qué devuelve antes de programarla. Si no sabes decirlo, todavía no sabes qué método usar.
Paso 3 · El catálogo de verdad · 40 min
Es el trabajo central de la sesión. Reescribe js/datos.js:
- Convierte tus arrays paralelos en un array de al menos ocho objetos, con
id,nombre,precio,categoria,stockydescripcion. - Adapta las funciones del paso 2 a la nueva estructura.
- Escribe
describir(producto)usando desestructuración en el parámetro. - Añade a algún producto una propiedad anidada y accede a ella con encadenamiento opcional.
- Escribe una función que devuelva una copia del catálogo con todos los precios subidos un 5 %, sin tocar el original. Compruébalo imprimiendo los dos.
Consultar · modificar, añadir y recorrer propiedades
producto.stock = 3; // modificar
producto.descuento = 0.1; // añadir
delete producto.descuento; // borrar
Object.keys(producto); // ["id", "nombre", "precio", ...]
Object.values(producto);
Object.entries(producto); // pares [clave, valor]
Paso 4 · JSON de ida y vuelta · 20 min
- Convierte el catálogo a JSON con sangría y obsérvalo.
- Vuelve a convertirlo a objeto y comprueba que todo sigue igual.
- Rompe el JSON a propósito de cuatro formas distintas —una comilla simple, una coma final, un comentario y una clave sin comillas— y anota qué dice el error en cada caso.
- Añade al catálogo un producto con una propiedad de valor
undefinedy otra que sea una función. Conviértelo a JSON y mira qué ha pasado con ellas. No se trata de un error, sino de una regla del formato que conviene conocer antes de la UD6.
Paso 5 · Tus funciones frente a los métodos · 20 min
En la sesión 3 escribiste aplicarATodos, filtrarPor, contarSi y, si hiciste la ampliación, reducir. Ponlas al lado de map, filter y reduce.
| Tu función | Método equivalente | ¿Devuelven lo mismo? | ¿Qué hace el método que la tuya no? |
|---|---|---|---|
aplicarATodos |
map |
||
filtrarPor |
filter |
||
contarSi |
|||
reducir |
reduce |
La tercera fila tiene truco: no hay un método que se llame así. Decide con qué lo escribirías y por qué.
Al terminar, responde en tres líneas: ahora que existen los métodos, ¿para qué ha servido escribir las tuyas a mano?
Ampliación si has completado el trabajo
Primero termina y comprueba los cinco pasos. El primer reto usa reduce para algo que no es sumar; el segundo enseña por qué el catálogo tenía que dejar de ser listas paralelas.
Reto 1 · reduce no sirve solo para sumar
El acumulador de reduce no tiene por qué ser un número: puede ser un array o un objeto, y ahí es donde el método deja de ser una curiosidad.
- Agrupa por categoría. Escribe
agruparPorCategoria(productos)que devuelva un objeto donde cada clave es una categoría y su valor, el array de productos de esa categoría. Conreduce, en una sola pasada. - Indexa por id. Escribe
indexarPorId(productos)que devuelva un objeto donde la clave es elid. Explica por qué eso hace que buscar un producto sea inmediato, y qué pierdes respecto al array. - Cuenta por categoría. Ahora el acumulador es un objeto de números.
- Resume en una pasada. Escribe una función que devuelva a la vez el total, el máximo, el mínimo y el recuento, recorriendo la lista una sola vez. El acumulador es un objeto con cuatro campos.
- Compara el resultado del apartado 4 con hacer cuatro recorridos separados. ¿Cuál se lee mejor? ¿Cuál harías en un catálogo de ocho productos y cuál en uno de cincuenta mil?
Reto 2 · Las listas que se desincronizan
Este código funcionaba perfectamente hasta que alguien retiró un producto del catálogo.
const nombres = ["Teclado", "Ratón", "Monitor", "Webcam"];
const precios = [89.9, 24.5, 199, 69];
const stocks = [4, 0, 7, 2];
function retirar(indice) {
nombres.splice(indice, 1);
precios.splice(indice, 1);
}
retirar(1);
for (let i = 0; i < nombres.length; i++) {
console.log(`${nombres[i]}: ${precios[i]} € · ${stocks[i]} unidades`);
}
- Ejecútalo y anota qué imprime. Ninguna línea da error, y tres de las tres son falsas.
- Explica exactamente qué se ha desincronizado y por qué el bucle no puede detectarlo.
- Reescríbelo con un array de objetos y comprueba que el mismo
retirarya no puede producir ese fallo. Enuncia por qué en una frase. - Una defensa intermedia: escribe una comprobación que, con las listas paralelas, hubiera avisado del problema al arrancar. ¿Qué tendría que comparar?
- Este fallo tiene nombre en la disciplina: los datos de una misma entidad viven en sitios distintos y nada garantiza que se mantengan de acuerdo. Escribe en dos líneas por qué agrupar en un objeto lo elimina de raíz en lugar de detectarlo.
reduce en una sola pasada, y las listas paralelas desincronizadas explicadas y corregidas.Cierre
5 minutos · comprobación y recuerdo
Lista de verificación de la sesión
- Tu catálogo es un array de objetos, no listas paralelas.
- Distingues los métodos que mutan de los que devuelven, y copias antes de ordenar.
- Eliges el método por lo que devuelve, no por costumbre.
- Pasas función de comparación a
sortcon números. - Sabes copiar un objeto y por qué hace falta.
- Conviertes a JSON y desde JSON, y sabes qué reglas tiene.
Antes de cerrar · 3 minutos, sin mirar
- Nombra dos métodos que muten y dos que no.
- ¿Qué hace
[...lista]? - ¿Qué devuelve
findsi no encuentra nada? - ¿Por qué
[10, 9, 100].sort()da un orden raro? - ¿Se puede modificar un objeto declarado con
const? - ¿Qué tres reglas de JSON son más estrictas que las de JavaScript?
Ver respuestas
1 · Mutan push y sort; no mutan slice y map.
2 · Crea un array nuevo con los mismos elementos: una copia superficial.
3 · undefined, y hay que comprobarlo antes de usar el resultado.
4 · Porque sin comparador ordena como texto, y "100" va antes que "9".
5 · Sí: const impide reasignar la variable, no modificar el objeto al que apunta.
6 · Las claves entre comillas dobles, sin comas finales y sin comentarios. Tampoco admite funciones ni undefined.
Microprueba semanal 4 · 5–10 minutos
Individual, sin IA y sin apuntes.
- Di qué método usarías para: los productos caros, el primero caro, saber si hay alguno caro, y el valor total del almacén.
- Ordena una lista de números de menor a mayor sin alterar la original.
- Escribe el JSON de un producto con tres campos, respetando sus reglas.