Aplanar Arreglos Anidados: flat() y flatMap()

javascript

🥞 Arreglos Dentro de Arreglos

A veces nos encontramos con arreglos que contienen otros arreglos como elementos. A esto se le llama un arreglo anidado.

let matriz = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let listas = ["a", "b", ["c", "d"], "e"];

En ciertas situaciones, queremos “deshacer” esta anidación y obtener un solo arreglo con todos los elementos. A esto le llamamos aplanar (flattening).

flat(): Aplanando por Niveles

El método flat() crea un nuevo arreglo con todos los elementos de los sub-arreglos concatenados recursivamente hasta una profundidad especificada.

Aplanado Básico (Profundidad 1)

Por defecto, flat() aplana solo un nivel de profundidad.

let arregloSimpleAnidado = [1, 2, [3, 4], 5];
let aplanadoSimple = arregloSimpleAnidado.flat();
console.log(aplanadoSimple); // Muestra [1, 2, 3, 4, 5]
let masAnidado = [1, [2, [3, [4]]], 5];
let aplanadoNivel1 = masAnidado.flat(); // Solo aplana el primer nivel
console.log(aplanadoNivel1); // Muestra [1, 2, [3, [4]], 5]

Especificando la Profundidad

Podemos pasar un argumento numérico a flat() para indicar cuántos niveles queremos aplanar.

let muyAnidado = [1, [2, [3, [4, [5]]]]];
let aplanadoNivel2 = muyAnidado.flat(2);
console.log(aplanadoNivel2); // Muestra [1, 2, 3, [4, [5]]]
let aplanadoNivel3 = muyAnidado.flat(3);
console.log(aplanadoNivel3); // Muestra [1, 2, 3, 4, [5]]

Aplanado Completo (Infinity)

Si queremos aplanar todos los niveles, sin importar cuán profundo sea el anidamiento, podemos pasar Infinity como argumento.

let superAnidado = [1, [2, [3, [4, [5, [6]]]]]];
let aplanadoTotal = superAnidado.flat(Infinity);
console.log(aplanadoTotal); // Muestra [1, 2, 3, 4, 5, 6]
INFO

💡 flat() también elimina los “empty slots” o agujeros del arreglo durante el aplanado.

let conAgujeros = [1, , 3, [4, , 6]];
console.log(conAgujeros.flat()); // Muestra [1, 3, 4, 6]

flatMap(): Mapear y Aplanar en Uno

Es muy común querer aplicar una función a cada elemento de un arreglo (como con map()) y luego aplanar el resultado inmediatamente (porque la función de mapeo podría devolver arreglos).

El método flatMap() hace exactamente eso: primero mapea cada elemento usando una función, y luego aplana el resultado en un nivel de profundidad. Es equivalente a llamar map() seguido de flat(1), pero es más eficiente.

let frases = ["Hola mundo", "Adiós amigos"];
// Queremos un arreglo con todas las palabras individuales
// Usando map y flat por separado:
let palabrasMap = frases.map((frase) => frase.split(" ")); // Devuelve [["Hola", "mundo"], ["Adiós", "amigos"]]
let palabrasFlat = palabrasMap.flat(); // Devuelve ["Hola", "mundo", "Adiós", "amigos"]
console.log(palabrasFlat);
// Usando flatMap:
let palabrasFlatMap = frases.flatMap((frase) => frase.split(" ")); // Hace ambos pasos
console.log(palabrasFlatMap); // Muestra ["Hola", "mundo", "Adiós", "amigos"]

Otro ejemplo: duplicar cada número en un arreglo.

let numeros = [1, 2, 3];
let duplicados = numeros.flatMap((num) => [num, num * 2]);
// Paso 1 (map imaginario): [[1, 2], [2, 4], [3, 6]]
// Paso 2 (flat(1)): [1, 2, 2, 4, 3, 6]
console.log(duplicados);
AVISO

⚠️ Recuerda que flatMap() solo aplana un nivel. Si tu función de mapeo devuelve arreglos muy anidados, flatMap() no los aplanará por completo.


☝️🤓
🏋️‍♂️ Ejercicio

Dado el arreglo: let data = [[1, 2], [3, 4, 5], [], [6]];

  1. Usa flat() para crear un nuevo arreglo dataAplanada que contenga todos los números en un solo nivel.
  2. Muestra dataAplanada por consola.
🏋️‍♂️ Ejercicio

Dado el arreglo: let items = [ { nombre: "A", tags: ["t1", "t2"] }, { nombre: "B", tags: ["t3"] }, { nombre: "C", tags: ["t2", "t4"] } ];

  1. Usa flatMap() para obtener un único arreglo todosLosTags que contenga todos los tags de todos los items, sin duplicados si es posible (pista: puedes usar Set después del flatMap o investigar cómo hacerlo directamente).
  2. Muestra todosLosTags por consola.
🚀 Extra

Investiga sobre:

  1. ¿Cuál es la ganancia de rendimiento de flatMap() comparado con map().flat()?
  2. ¿Cómo podrías implementar tu propia función miFlat(arreglo, profundidad) que imite el comportamiento de flat() usando recursión o iteración?
  3. Considera el arreglo [1, 2, , 4, [5, , 7]]. ¿Cuál sería el resultado de aplicar flat() y flatMap(x => [x * 2])? Analiza el manejo de los empty slots.

Comentarios

Artículos relacionados

javascript

Ordenar y Revertir el Orden de los Arreglos

5 mins
javascript

Arreglos - Métodos de iteración

3 mins
javascript

Desestructuración de Arreglos en JavaScript

4 mins