Dominando conceptos: Arreglos

¿Qué son los Arreglos?

🎯 Introducción

Hay ocasiones en las que queremos guardar una lista de compras, los nombres de usuarios, o puntuaciones para algún un juego. ¿Cómo podríamos almacenar todos esos datos de una forma organizada?

En esta ocasión aprenderemos sobre los arreglos (o arrays en inglés).

Analogía

Imagina una fila de casilleros. Cada casillero tiene un número único que lo identifica, comenzando desde el número 0.

Dentro de cada casillero, puedes guardar cosas. Si quieres guardar tu mochila, la pones en el casillero 0; si quieres guardar tu abrigo, en el casillero 1, y así sucesivamente.

Los arreglos funcionan de manera similar: son como estos casilleros donde cada “espacio” tiene un índice numérico y puede contener un valor.

// Ejemplo de una lista de compras como arreglo
let listaDeCompras = ["Leche", "Pan", "Huevos", "Manzanas"];
// Ejemplo de puntuaciones
let puntuaciones = [150, 230, 95, 410];
// Un arreglo puede contener diferentes tipos de datos
let datosVariados = ["Hola", 42, true, null, "Mundo"];
INFO

Otros lenguajes de programación utilizan otro nombre para los arreglos, como List (Listas). También puede que tengan diferencias técnicas, aunque el principio es similar.

🤔 ¿Por qué usar Arreglos?

Los arreglos son increíblemente útiles porque nos permiten:

  1. Agrupar datos relacionados: Mantener juntos elementos que pertenecen a la misma categoría (ej. todos los productos de una tienda, todos los estudiantes de una clase).
  2. Acceder a elementos fácilmente: Podemos obtener un elemento específico de la lista usando su índice (el número que le corresponde).
  3. Manipular la colección: Podemos agregar, eliminar, o modificar elementos de la lista de forma sencilla usando métodos específicos para arreglos.
  4. Iterar sobre los elementos: Podemos recorrer fácilmente todos los elementos de la lista para realizar operaciones en cada uno (ej. mostrar todos los nombres, calcular el total de las puntuaciones).
INFO

“Iterar” sólo es una palabra técnica para decir “repetir [instrucciones]“

⚙️ Características Principales

  • Orden: Los elementos mantienen el orden en el que fueron insertados. El primer elemento siempre estará en la posición 0, el segundo en la 1, y así sucesivamente.
  • Dinamismo: Generalmente, podemos agregar o quitar elementos, y el tamaño del arreglo se ajustará automáticamente.
  • Flexibilidad: En JavaScript, un mismo arreglo puede guardar números, strings, booleanos, objetos, ¡e incluso otros arreglos!

En otros lenguajes esta estructura de datos no es tan flexible y sólo puede guardar un tipo de dato. Por ejemplo, una lista sólo de números.

En los próximos artículos, veremos cómo crear, acceder y manipularlos.


☝️🤓
🏋️‍♂️ Ejercicio

Piensa en 3 situaciones de la vida cotidiana donde podrías usar un arreglo para organizar información.

Describe qué tipo de datos guardarías en cada arreglo.

Ejemplo

Situación: Registro de temperaturas diarias de una semana.

Tipo de dato: Números [25, 26, 24, 27, 28, 26, 25]

🏋️‍♂️ Ejercicio

¿Cuál crees que es la principal ventaja de usar un arreglo en lugar de variables separadas para guardar una colección de datos (como puntuacion1, puntuacion2, puntuacion3)?

Explica tu razonamiento.

🚀 Extra

Investiga sobre:

  1. ¿Existen otros tipos de “listas” o “colecciones” en JavaScript además de los arreglos? (Pista: Set, Map)
  2. ¿Qué significa que los arreglos en JavaScript sean “objetos”?

Crear y Acceder a Elementos de Arreglos

🛠️ Creando Arreglos

Ahora que sabemos qué son los arreglos, veamos cómo podemos crearlos en JavaScript.

1. Arreglos Literales (La forma más común)

La manera recomendada es usar corchetes [] y separar los elementos con comas ,:

// Un arreglo vacío
let tareasPendientes = [];
// Un arreglo de números
let numerosFavoritos = [7, 13, 42];
// Un arreglo de strings
let coloresPrimarios = ["Rojo", "Verde", "Azul"];
// Un arreglo mixto
let misCosasFavoritas = ["Fido", 3, "Perro", true];

2. Con el constructor new Array()

También podemos usar la sintaxis new Array(). Podríamos verlo como una fábrica, vamos directo con ellos y les pedimos que nos hagan un arreglo con los elementos deseados.

// Crear un arreglo con elementos
let frutas = new Array("Manzana", "Banana", "Naranja");
// Equivale a:
// let frutas = ["Manzana", "Banana", "Naranja"];

Sin embargo, tiene algunos comportamientos que pueden ser confusos. Si pasamos un solo número, crea un arreglo vacío con esa longitud

let arregloVacioLargo = new Array(5);
// Crea [ , , , , ] (un arreglo con 5 espacios vacíos)
console.log(arregloVacioLargo.length); // Muestra 5
console.log(arregloVacioLargo[0]); // Muestra undefined

AVISO

⚠️ Por la confusión que puede generar new Array(numero), es mejor usar [] para crear arreglos.

🔍 Accediendo a Elementos

Una vez que tenemos un arreglo, ¿cómo obtenemos un valor específico?

Usamos el índice del elemento. ¿Recuerdas la analogía de los casilleros? Se numeran empezando por el 0.

Para acceder a un elemento, escribimos el nombre del arreglo seguido de corchetes [] con el número del índice dentro.

let herramientas = ["Martillo", "Destornillador", "Llave inglesa", "Sierra"];
// Acceder al primer elemento
console.log(herramientas[0]); // Muestra "Martillo"
// Acceder al tercer elemento
console.log(herramientas[2]); // Muestra "Llave inglesa"

¿Qué pasa si intento acceder a un índice que no existe?

Si intentas usar un índice que está fuera de los límites del arreglo (mayor o igual a su longitud, o negativo), JavaScript devolverá undefined.

console.log(herramientas[4]); // Muestra undefined (no hay elemento en el índice 4)
console.log(herramientas[-1]); // Muestra undefined (los índices negativos no funcionan así por defecto)

✏️ Modificando Elementos

Podemos cambiar el valor de un elemento existente de la misma forma que accedemos a él, usando el operador de asignación =.

let calificaciones = [7, 8, 5];
console.log(calificaciones[2]); // Muestra 5
// Cambiamos la calificación en el índice 2
calificaciones[2] = 9;
console.log(calificaciones[2]); // Muestra 9
console.log(calificaciones); // Muestra [7, 8, 9]

CUIDADO

Si asignamos un valor a un índice que está más allá del final del arreglo, JavaScript agrandará el arreglo y llenará los espacios intermedios con undefined (o valores vacíos llamados empty slots).

let numeros = [10, 20];
numeros[3] = 40; // Asignamos al índice 3
console.log(numeros); // Muestra [10, 20, <1 empty item>, 40]
console.log(numeros[2]); // Muestra undefined
console.log(numeros.length); // Muestra 4

Esto puede llevar a errores, ¡así que ten cuidado!


☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea un arreglo llamado planetas que contenga los nombres de los planetas del sistema solar en orden desde el Sol.
  2. Muestra por consola el nombre del tercer planeta.
  3. Muestra por consola el nombre del último planeta.
  4. Crea una variable llamada planetaFavorito y asígnale el valor del quinto planeta del arreglo.
  5. Muestra la variable planetaFavorito por consola.
🏋️‍♂️ Ejercicio
  1. Crea un arreglo llamado inventario con los siguientes strings: "Manzanas", "Plátanos", "Naranjas".
  2. Cambia el segundo elemento (Plátanos) por "Fresas".
  3. Muestra el arreglo completo por consola para verificar el cambio.
  4. Intenta mostrar por consola el elemento en el índice 1
  5. ¿Qué resultado obtienes y por qué?
🚀 Extra

Investiga sobre:

  1. ¿Qué son los “empty slots” o “agujeros” (holes) en los arreglos de JavaScript y por qué es mejor evitarlos?
  2. ¿Puedes tener un arreglo dentro de otro arreglo? ¿Cómo accederías a un elemento del arreglo interno?
  3. ¿Hay alguna forma de acceder al último elemento de un arreglo sin saber su longitud exacta de antemano? (Pista: método .at())

La Propiedad length

📏 ¿Cuántos Elementos Hay?

Todos los arreglos en JavaScript tienen una propiedad especial llamada length.

INFO

La palabra length viene del inglés, que significa largo o longitud

Esta propiedad nos dice cuántos elementos contiene el arreglo en ese momento.

Sólo debemos poner un punto frente al arreglo . y luego la palabra length.

let misNumeros = [5, 10, 15, 20];
console.log(misNumeros.length); // Muestra 4
let ingredientesPizza = ["Masa", "Tomate", "Queso"];
console.log(ingredientesPizza.length); // Muestra 3
let arregloVacio = [];
console.log(arregloVacio.length); // Muestra 0

Terminal window
4
3
0

✨ Se Actualiza Automáticamente

Se actualiza sola cada vez que agregamos o quitamos elementos del arreglo usando los métodos apropiados (que veremos más adelante, como push, pop, splice, etc.).

let amigos = ["Ana", "Luis"];
console.log(amigos.length); // Muestra 2
// Agregamos un amigo (usando un método que pronto aprenderás)
amigos[2] = "Carlos";
console.log(amigos); // Muestra ["Ana", "Luis", "Carlos"]
console.log(amigos.length); // Muestra 3 (¡se actualizó!)

⏭️ Cómo Acceder al Último Elemento Usando length

Sabemos que los índices para acceder al contenido de un arreglo empiezan desde 0, ya que length nos devuelve el conteo total en “formato humano”, el índice del último elemento en “formato arreglo” siempre será length - 1.

let episodios = ["El Despertar", "Secretos Ocultos", "La Traición", "El Final"];
let ultimoEpisodio = episodios[episodios.length - 1];
console.log(ultimoEpisodio);

Terminal window
El Final

Esta es una forma muy común de obtener el último elemento, especialmente cuando no sabemos cuántos elementos tendrá el arreglo.

INFO

JavaScript moderno introdujo el método .at(-1) que es una forma más directa y legible de obtener el último elemento

⚠️ Modificando length Directamente

También podemos asignar un valor a la propiedad length. Esto modifica el arreglo de formas que pueden ser útiles, pero también peligrosas si no se entienden bien.

Acortando un Arreglo

Si asignas a length un número menor que la cantidad actual de elementos, el arreglo se truncará, eliminando los elementos del final.

let letras = ["a", "b", "c", "d", "e"];
console.log(letras.length); // Muestra 5
letras.length = 3; // Asignamos un valor menor
console.log(letras); // Muestra ['a', 'b', 'c']
console.log(letras.length); // Muestra 3

Esta es una forma rápida de eliminar elementos del final.

Agrandando un Arreglo

Si asignas a length un número mayor que la cantidad actual de elementos, el arreglo se agrandará, pero los nuevos espacios se llenarán con “empty slots” o “agujeros”, que se comportan de forma similar a undefined pero no son exactamente lo mismo.

let items = [100, 200];
console.log(items.length); // Muestra 2
items.length = 5; // Asignamos un valor mayor
console.log(items); // Muestra [100, 200, <3 empty items>]
console.log(items.length); // Muestra 5
console.log(items[2]); // Muestra undefined
CUIDADO

Crear arreglos con “empty slots” (agujeros) generalmente no es recomendable. Puede hacer que algunos métodos de arreglo se comporten de forma inesperada.

🧹 Vaciar un Arreglo

Una forma rápida de eliminar todos los elementos de un arreglo es asignar 0 a su propiedad length.

let listaTareas = ["Lavar ropa", "Comprar pan", "Estudiar JS"];
console.log(listaTareas.length); // Muestra 3
listaTareas.length = 0; // ¡Arreglo vaciado!
console.log(listaTareas); // Muestra []
console.log(listaTareas.length); // Muestra 0

☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea un arreglo llamado frutas con 5 nombres de frutas.
  2. Muestra por consola cuántas frutas hay en el arreglo usando length.
  3. Usando length, muestra por consola la última fruta del arreglo.
  4. Ahora, cambia el length del arreglo a 2.
  5. Muestra el arreglo frutas por consola. ¿Qué frutas quedaron?
🏋️‍♂️ Ejercicio

Dado el siguiente arreglo: let valores = [10, true, "Hola", null, 50];

  1. ¿Cuál es el valor de valores.length?
  2. ¿Cuál es el índice del elemento null?
  3. Escribe el código para acceder al elemento "Hola" usando la propiedad length (sin usar directamente el índice 2).
  4. ¿Qué pasaría si hiciéramos valores.length = 10? Describe cómo quedaría el arreglo.
🚀 Extra

Investiga sobre:

  1. El método .at() para acceder a elementos. ¿Cómo usarías miArreglo.at(-1) y qué ventaja tiene sobre miArreglo[miArreglo.length - 1]?
  2. ¿Cuál es la diferencia exacta entre un elemento undefined y un “empty slot” en un arreglo?
  3. ¿Qué otros métodos existen para vaciar un arreglo además de miArreglo.length = 0?

Arreglos - Métodos para agregar y quitar elementos

Cuando trabajamos con Array en JavaScript, es común tener que agregar o eliminar elementos.

Existen algunos métodos que nos pueden ayudar con estas tareas.

Resumen

MétodoOperaciónDevuelve
pop()Elimina el último elementoEl elemento eliminado
push()Añade elementos al finallength
shift()Elimina el primer elementoEl elemento eliminado
unshift()Añade elementos al iniciolength

pop()

Elimina el último elemento de un arreglo y lo devuelve.

const frutas = ["🍎", "🍌", "🍊"];
const ultimaFruta = frutas.pop();
console.log(frutas);
console.log(ultimaFruta);

Terminal window
['🍎', '🍌']
'🍊'

push()

Añade uno o más elementos al final del arreglo y devuelve length.

const letras = ["a", "b", "c"];
const nuevaLongitud = letras.push("d");
console.log(letras);
console.log(nuevaLongitud);

Terminal window
["a", "b", "c", "d"]
4
INFO

Para añadir más de un elemento, se deben separar por comas (,)

const letras = ["a", "b", "c"];
const nuevaLongitud = letras.push("d", "e");
console.log(letras);
console.log(nuevaLongitud);

Terminal window
["a", "b", "c", "d", "e"]
5

shift()

Elimina el primer elemento del arreglo y lo devuelve.

const colores = ["🟥", "🟦", "🟩"];
const primerColor = colores.shift();
console.log(colores);
console.log(primerColor);

Terminal window
['🟦', '🟩']
'🟥'

unshift()

Añade uno o más elementos al principio del arreglo y devuelve length.

const tareas = ["🧹", "🍳"];
const nuevaLongitud = tareas.unshift("📖");
console.log(tareas);
console.log(nuevaLongitud);

Terminal window
['📖', '🧹', '🍳']
3
INFO

Para añadir más de un elemento, se deben separar por comas (,)

const tareas = ["🧹", "🍳"];
const nuevaLongitud = tareas.unshift("📖", "🏋️");
console.log(tareas);
console.log(nuevaLongitud);

Terminal window
['📖', '🏋️', '🧹', '🍳']
4

Extraer y Modificar Partes de Arreglos: slice() y splice()

🔪 slice(): Extrayendo una Porción (Sin Modificar)

El método slice() (“rebanar”) nos permite crear una copia superficial de una porción de un arreglo existente, sin modificar el arreglo original. Es como clonar sólo una parte del arreglo, de esta manera no afectamos el original.

Funciona con dos argumentos opcionales:

  1. inicio: El índice donde comienza la extracción (incluido).
  2. fin: El índice donde termina la extracción (excluido).
let animales = ["Perro", "Gato", "Conejo", "Hamster", "Pez"];
// Extraer desde el índice 1 hasta el 3 (sin incluir el 3)
let mamiferosPequeños = animales.slice(1, 3);
console.log(mamiferosPequeños); // Muestra ["Gato", "Conejo"]
// Extraer desde el índice 2 hasta el final
let roedoresYPeces = animales.slice(2);
console.log(roedoresYPeces); // Muestra ["Conejo", "Hamster", "Pez"]
// Extraer los últimos 2 elementos (usando índice negativo)
let ultimosDos = animales.slice(-2);
console.log(ultimosDos); // Muestra ["Hamster", "Pez"]
// Crear una copia completa del arreglo
let copiaAnimales = animales.slice();
console.log(copiaAnimales); // Muestra ["Perro", "Gato", "Conejo", "Hamster", "Pez"]
// IMPORTANTE: El arreglo original NO cambia
console.log(animales); // Muestra ["Perro", "Gato", "Conejo", "Hamster", "Pez"]
INFO

💡 slice() es ideal cuando necesitas una parte de un arreglo para trabajar con ella sin riesgo de alterar el original.

splice(): Modificando el Arreglo Original

El método splice() (“empalmar” o “insertar”) es mucho más poderoso (¡y potencialmente destructivo!). Modifica el contenido de un arreglo eliminando, reemplazando o agregando elementos nuevos directamente en el original.

También devuelve un arreglo con los elementos eliminados, si los hubiera.

Funciona con al menos un argumento, y opcionalmente más:

  1. inicio: El índice donde comenzar a cambiar el arreglo.
  2. cantidadAEliminar (opcional): El número de elementos a eliminar desde inicio.
  3. elemento1, elemento2, ... (opcional): Los elementos a agregar al arreglo, comenzando desde inicio.

Eliminar Elementos

let letras = ["a", "b", "c", "d", "e"];
// Eliminar 1 elemento desde el índice 2 ('c')
let letrasEliminadas = letras.splice(2, 1);
console.log(letras); // Muestra ['a', 'b', 'd', 'e'] (¡Original modificado!)
console.log(letrasEliminadas); // Muestra ['c']
// Eliminar 2 elementos desde el índice 1 ('b', 'd')
let masEliminadas = letras.splice(1, 2);
console.log(letras); // Muestra ['a', 'e']
console.log(masEliminadas); // Muestra ['b', 'd']

Reemplazar Elementos

Simplemente proporciona los nuevos elementos después de la cantidad a eliminar.

let numeros = [1, 2, 3, 4, 5];
// Reemplazar 1 elemento en el índice 1 (el 2) por 99
let reemplazados = numeros.splice(1, 1, 99);
console.log(numeros); // Muestra [1, 99, 3, 4, 5]
console.log(reemplazados); // Muestra [2]

Agregar Elementos (sin eliminar)

Establece cantidadAEliminar en 0.

let colores = ["Rojo", "Verde", "Azul"];
// Agregar "Amarillo" y "Naranja" en el índice 1 (sin eliminar nada)
colores.splice(1, 0, "Amarillo", "Naranja");
console.log(colores); // Muestra ["Rojo", "Amarillo", "Naranja", "Verde", "Azul"]
CUIDADO

🔥 ¡Recuerda! splice() SIEMPRE modifica el arreglo original. Úsalo con cuidado, especialmente si otras partes de tu código dependen de ese arreglo.

🤔 ¿Cuándo Usar Cuál?

  • Usa slice() cuando necesites obtener una copia de una parte (o todo) del arreglo sin afectar el original.
  • Usa splice() cuando necesites modificar el arreglo original eliminando, agregando o reemplazando elementos en una posición específica.

☝️🤓
🏋️‍♂️ Ejercicio

Dado el arreglo: let codigo = ["React", "Vue", "Angular", "Svelte", "Solid"];

  1. Usa slice() para crear un nuevo arreglo frameworksPopulares que contenga solo “React”, “Vue” y “Angular”.
  2. Usa slice() para crear un nuevo arreglo ultimosFrameworks que contenga los últimos dos elementos del arreglo codigo.
  3. Verifica que el arreglo codigo original no haya cambiado después de las operaciones anteriores.
🏋️‍♂️ Ejercicio

Dado el arreglo: let tareas = ["Estudiar", "Comer", "Dormir"];

  1. Usa splice() para insertar la tarea "Trabajar" después de "Estudiar".
  2. Usa splice() para reemplazar "Dormir" con "Jugar" y "Relajarse".
  3. Usa splice() para eliminar "Comer" del arreglo.
  4. Muestra el arreglo tareas final después de todas las modificaciones.
🚀 Extra

Investiga sobre:

  1. ¿Qué significa que slice() crea una “copia superficial” (shallow copy)? ¿Qué implicaciones tiene si el arreglo contiene objetos u otros arreglos?
  2. ¿Cómo podrías usar splice() para vaciar completamente un arreglo? Compara esta forma con miArreglo.length = 0.
  3. Los métodos más nuevos toSpliced(). ¿Qué problema resuelve comparado con splice()?

Formas de Copiar Arreglos en JavaScript

🤔 ¿Por Qué Copiar un Arreglo?

A veces, queremos trabajar con los datos de un arreglo sin modificar el original. Si simplemente asignamos un arreglo a otra variable, no estamos creando una copia, sino que ambas variables apuntarán al mismo arreglo en la memoria.

let original = [1, 2, 3];
let referencia = original; // NO es una copia
referencia.push(4);
console.log(original); // Muestra [1, 2, 3, 4] ¡El original fue modificado!
console.log(referencia); // Muestra [1, 2, 3, 4]

Para evitar esto, necesitamos crear una copia independiente.

📋 Copias Superficiales (Shallow Copies)

La mayoría de los métodos integrados crean copias superficiales. Esto significa que copian los elementos del primer nivel del arreglo. Si esos elementos son valores primitivos (números, strings, booleanos), se copian por valor y son independientes. Pero si los elementos son objetos o arreglos, se copia la referencia a esos objetos/arreglos, no los objetos/arreglos en sí.

1. Usando slice()

Como vimos, llamar a slice() sin argumentos crea una copia superficial de todo el arreglo.

let numeros = [10, 20, 30];
let copiaNumeros = numeros.slice();
copiaNumeros.push(40);
console.log(numeros); // Muestra [10, 20, 30] (Original intacto)
console.log(copiaNumeros); // Muestra [10, 20, 30, 40]

2. Usando el Operador Spread (...)

El operador Spread es una forma moderna y muy legible de crear copias superficiales.

let colores = ["Rojo", "Verde", "Azul"];
let copiaColores = [...colores];
copiaColores[0] = "Morado";
console.log(colores); // Muestra ["Rojo", "Verde", "Azul"]
console.log(copiaColores); // Muestra ["Morado", "Verde", "Azul"]

3. Usando Array.from()

Array.from() crea una nueva instancia de Array a partir de un objeto iterable (como otro arreglo).

let herramientas = ["Martillo", "Sierra"];
let copiaHerramientas = Array.from(herramientas);
copiaHerramientas.pop(); // Quita "Sierra"
console.log(herramientas); // Muestra ["Martillo", "Sierra"]
console.log(copiaHerramientas); // Muestra ["Martillo"]

El Problema de las Copias Superficiales con Objetos/Arreglos Anidados

Si el arreglo contiene objetos o más arreglos, la copia superficial solo copia las referencias:

let originalAnidado = [{ id: 1 }, { id: 2 }];
let copiaSuperficial = [...originalAnidado];
// Modificamos un objeto DENTRO de la copia
copiaSuperficial[0].id = 99;
// ¡El cambio se refleja en el original también!
console.log(originalAnidado[0].id); // Muestra 99
console.log(copiaSuperficial[0].id); // Muestra 99
AVISO

⚠️ ¡Cuidado! Con arreglos anidados u objetos dentro de arreglos, slice(), ... y Array.from() no crean copias independientes de esos elementos internos.

🧱 Copias Profundas (Deep Copies)

Para crear una copia completamente independiente, incluyendo objetos y arreglos anidados, necesitamos una copia profunda. No hay un método integrado simple y universal para esto, pero hay técnicas comunes:

1. Usando JSON.stringify() y JSON.parse()

Una forma común (con limitaciones) es convertir el arreglo a un string JSON y luego de vuelta a un objeto/arreglo JavaScript.

let originalProfundo = [{ valor: 10 }, [{ sub: 20 }]];
let copiaProfunda = JSON.parse(JSON.stringify(originalProfundo));
// Modificamos un objeto anidado en la copia
copiaProfunda[1][0].sub = 500;
// El original NO se ve afectado
console.log(originalProfundo[1][0].sub); // Muestra 20
console.log(copiaProfunda[1][0].sub); // Muestra 500

Limitaciones: Este método no funciona bien con ciertos tipos de datos como Date, RegExp, Map, Set, funciones, o undefined, ya que se pierden o transforman durante la serialización JSON.

2. Usando structuredClone() (Moderno)

La forma más moderna y robusta es usar la función global structuredClone().

let datosOriginales = [{ fecha: new Date() }, { set: new Set([1, 2]) }];
let copiaEstructurada = structuredClone(datosOriginales);
// Modificamos el Set en la copia
copiaEstructurada[1].set.add(3);
// El original NO se ve afectado
console.log(datosOriginales[1].set); // Muestra Set(2) { 1, 2 }
console.log(copiaEstructurada[1].set); // Muestra Set(3) { 1, 2, 3 }
INFO

💡 structuredClone() es la mejor opción moderna para crear copias profundas, ya que maneja más tipos de datos que el truco de JSON.


☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea un arreglo miArray = [5, 10, 15]. 2. Crea una referencia llamada refArray asignándole miArray. 3. Crea una copia superficial llamada copiaArray usando el operador Spread. 4. Modifica refArray agregando el número 20 al final (push(20)). 5. Modifica copiaArray cambiando el primer elemento a 99 (copiaArray[0] = 99). 6. Muestra por consola miArray, refArray y copiaArray. Explica por qué miArray cambió en un caso pero no en el otro.
🏋️‍♂️ Ejercicio

Dado el arreglo: let datos = [{ nombre: "Alice" }, { nombre: "Bob" }];

  1. Crea una copia superficial copia1 usando slice().
  2. Crea una copia profunda copia2 usando structuredClone().
  3. Cambia el nombre del primer objeto en copia1 a “Charlie” (copia1[0].nombre = "Charlie").
  4. Cambia el nombre del segundo objeto en copia2 a “David” (copia2[1].nombre = "David").
  5. Muestra por consola el arreglo datos original. ¿Cuál de las modificaciones (Charlie o David) afectó al original y por qué?
🚀 Extra

Investiga sobre: 1. Las limitaciones exactas del método JSON.parse(JSON.stringify()) para copias profundas (qué tipos de datos no maneja bien). 2. Bibliotecas de JavaScript populares (como Lodash) que ofrecen funciones para realizar copias profundas (ej. _.cloneDeep()). ¿Qué ventajas ofrecen sobre structuredClone() o el método JSON? 3. ¿Qué es la “copia por valor” vs “copia por referencia” en JavaScript?


Arreglos - Métodos de verificación

Cuando trabajamos con Array en JavaScript, habrá escenarios donde tengamos que encontrar elementos con ciertos criterios.

Resumen

Método¿Qué verifica?Devuelve true si…
every()Todos los elementos cumplen la condiciónTodos los elementos pasan el test del callback
includes()Existe un valor específico en el arregloEl arreglo contiene el valor buscado
some()Al menos un elemento cumple la condiciónAlgún elemento pasa el test del callback

every()

Verifica si todos los elementos de un arreglo cumplen con una condición.

Devuelve true si todos los elementos cumplen la condición, de lo contrario, devuelve false.

Por ejemplo: Si queremos verificar que todos los números en un arreglo son mayores que 10.

const numeros = [12, 15, 20, 25];
const todosMayoresQueDiez = numeros.every((num) => num > 10);
console.log(todosMayoresQueDiez);

Terminal window
true

Si cambiamos un número para que no cumpla la condición:

const numeros = [12, 15, 8, 25];
const todosMayoresQueDiez = numeros.every((num) => num > 10);
console.log(todosMayoresQueDiez);

Terminal window
false

includes()

Verifica si el arreglo contiene un valor específico.

Devuelve true si el valor existe en el arreglo, de lo contrario, devuelve false.

Por ejemplo: Tenemos una lista de frutas y queremos saber si contiene ”🍎” (manzana).

const frutas = ["🍎", "🍐", "🍌"];
const tieneManzana = frutas.includes("🍎");
console.log(tieneManzana);

Terminal window
true

Si buscamos un elemento que no está presente, como ”🥭” (mango):

const frutas = ["🍎", "🍐", "🍌"];
const tieneMango = frutas.includes("🥭");
console.log(tieneMango);

Terminal window
false

some()

Verifica si al menos un elemento del arreglo cumple con la condición específica.

Devuelve true si al menos un elemento cumple la condición, de lo contrario, devuelve false.

Por ejemplo: Si que queremos verificar que algún número del arreglo es par.

const numeros = [1, 3, 5, 8];
const tienePar = numeros.some((num) => num % 2 === 0);
console.log(tienePar);

Terminal window
true

Si no hay ningún número par:

const numeros = [1, 3, 5, 7];
const tienePar = numeros.some((num) => num % 2 === 0);
console.log(tienePar); // false

Terminal window
false

Arreglos - Métodos de iteración

Cuando trabajamos con Array en JavaScript, hay casos en los que debemos recorrerlos para aplicar operaciones sobre sus elementos.

Resumen

MétodoDescripciónUso principalDevuelve
forEach()Ejecuta una función por cada elemento del arreglo.Realizar acciones sobre cada elemento.undefined
map()Crea un nuevo arreglo aplicando una función a cada elemento del original.Procesar elementos del arreglo.Un nuevo arreglo con los valores transformados.
reduce()Reduce todos los elementos del arreglo a un único valor aplicando una función acumuladora.Acumular valores, como suma, concatenación, etc.El valor acumulado.

forEach()

Nos ayuda a ejecutar una función para cada elemento del arreglo.

const miFuncion = (elemento, indice, arreglo) => {
//...
};
miArreglo.forEach(miFuncion);
  • elemento: El elemento actual que se está procesando.
  • indice: El índice del elemento actual (opcional).
  • arreglo: El arreglo original (opcional).

Ejemplo

const numeros = [1, 2, 3, 4];
const imprimir = (numero) => {
console.log(numero);
};
numeros.forEach(imprimir);

Terminal window
1
2
3
4

map()

Nos ayuda a crear un nuevo arreglo aplicando una función a cada elemento del arreglo original.

Por lo tanto la función debe devolver un valor que será insertado en el nuevo arreglo.

Ideal cuando queremos tener valores procesados y preservar los valores del original, es decir, tener dos arreglos.

Sintaxis

const miFuncion = (elemento, indice, arreglo) => {
//...
};
miArreglo.map(miFuncion);
  • elemento: El elemento actual que se está procesando.
  • indice: El índice del elemento actual (opcional).
  • arreglo: El arreglo original (opcional).

Ejemplo

const numeros = [1, 2, 3, 4];
const duplicar = (num) => num * 2;
const resultado = numeros.map(duplicar);
console.log(numeros);
console.log(resultado);

Terminal window
[1, 2, 3, 4]
[2, 4, 6, 8]

reduce()

Toma cada elemento de un arreglo hasta reducirlos a un único valor acumulado.

Es poderoso para operaciones como sumar, concatenar o combinar elementos.

Sintaxis

const valorInicial = 0;
const miReductor = (acumulador, elemento, indice, arreglo) => {
//...
};
miArreglo.reduce(miReductor, valorInicial);
  • acumulador: La variable que guarda el resultado acumulado.
  • elemento: El elemento actual que se está procesando.
  • indice: El índice del elemento actual (opcional).
  • arreglo: El arreglo original (opcional).
  • valorInicial: El valor inicial del acumulador (opcional).

Ejemplo

const valorInicial = 0;
const numbers = [1, 2, 3, 4];
const sumatoria = (acc, num) => acc + num;
const resultado = numbers.reduce(sumatoria, valorInicial);
console.log(resultado);

Ordenar y Revertir el Orden de los Arreglos

🔄 reverse(): Invirtiendo el Orden

El método reverse() es simple: invierte el orden de los elementos en el arreglo. El primer elemento pasa a ser el último, el segundo pasa a ser el penúltimo, y así sucesivamente.

Importante: reverse() modifica el arreglo original y también devuelve una referencia a ese mismo arreglo modificado.

let numeros = [1, 2, 3, 4, 5];
console.log("Original:", numeros);
numeros.reverse(); // Invierte el arreglo
console.log("Revertido:", numeros); // Muestra [5, 4, 3, 2, 1]
let letras = ["a", "b", "c"];
letras.reverse();
console.log(letras); // Muestra ['c', 'b', 'a']

sort(): Ordenando Elementos

El método sort() ordena los elementos de un arreglo en su lugar (modifica el original) y devuelve una referencia al arreglo ordenado.

Comportamiento por Defecto (Orden Alfabético/Unicode)

Si llamas a sort() sin argumentos, convierte los elementos a strings y los ordena según sus valores de puntos de código Unicode (similar al orden alfabético).

let frutas = ["Banana", "Manzana", "Naranja", "Fresa"];
frutas.sort();
console.log(frutas); // Muestra ["Banana", "Fresa", "Manzana", "Naranja"]
let mezcla = ["Gato", "perro", "AVE", "pez"]; // Ojo con mayúsculas/minúsculas
mezcla.sort();
console.log(mezcla); // Muestra ["AVE", "Gato", "pez", "perro"]
// (Las mayúsculas suelen ir antes que las minúsculas en Unicode)
AVISO

⚠️ ¡El ordenamiento por defecto no funciona bien para números! Los convierte a strings y los ordena alfabéticamente.

let numerosDesordenados = [10, 5, 100, 2, 25];
numerosDesordenados.sort();
console.log(numerosDesordenados); // Muestra [10, 100, 2, 25, 5] (¡Incorrecto! "100" va antes que "2")

Ordenar Números: La Función de Comparación

Para ordenar números (o cualquier cosa que no sea un simple orden alfabético), debemos pasarle a sort() una función de comparación. Esta función recibe dos argumentos (a y b), que representan dos elementos del arreglo que se están comparando.

La función debe devolver:

  • Un número negativo si a debe ir antes que b.
  • Un número positivo si a debe ir después que b.
  • 0 si a y b son considerados iguales en orden.

Para ordenar números de menor a mayor, la función es simple: (a, b) => a - b.

let numerosParaOrdenar = [10, 5, 100, 2, 25];
// Ordenar de menor a mayor
numerosParaOrdenar.sort((a, b) => a - b);
console.log(numerosParaOrdenar); // Muestra [2, 5, 10, 25, 100]

¿Cómo funciona a - b?

  • Si a es menor que b, a - b es negativo (a va antes).
  • Si a es mayor que b, a - b es positivo (a va después).
  • Si son iguales, a - b es 0 (orden relativo no cambia).

Para ordenar de mayor a menor, simplemente invertimos la resta: (a, b) => b - a.

// Ordenar de mayor a menor
numerosParaOrdenar.sort((a, b) => b - a);
console.log(numerosParaOrdenar); // Muestra [100, 25, 10, 5, 2]

Ordenar Objetos

Podemos usar la función de comparación para ordenar arreglos de objetos basándonos en alguna de sus propiedades.

let productos = [
{ nombre: "Laptop", precio: 1200 },
{ nombre: "Teclado", precio: 75 },
{ nombre: "Monitor", precio: 300 },
];
// Ordenar por precio (menor a mayor)
productos.sort((a, b) => a.precio - b.precio);
console.log(productos);
/* Muestra:
[
{ nombre: "Teclado", precio: 75 },
{ nombre: "Monitor", precio: 300 },
{ nombre: "Laptop", precio: 1200 }
]
*/
// Ordenar por nombre (alfabético)
productos.sort((a, b) => {
if (a.nombre < b.nombre) return -1;
if (a.nombre > b.nombre) return 1;
return 0;
});
console.log(productos);
/* Muestra:
[
{ nombre: "Laptop", precio: 1200 },
{ nombre: "Monitor", precio: 300 },
{ nombre: "Teclado", precio: 75 }
]
*/
CUIDADO

🔥 Tanto sort() como reverse() modifican el arreglo original. Si necesitas conservar el original, crea una copia primero (usando slice() o ...) y luego ordena o revierte la copia.

let original = [3, 1, 4];
let copiaOrdenada = [...original].sort((a, b) => a - b); // Ordena la copia
console.log(original); // Muestra [3, 1, 4]
console.log(copiaOrdenada); // Muestra [1, 3, 4]

☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea un arreglo puntuaciones = [88, 95, 72, 100, 81].
  2. Ordena el arreglo puntuaciones de mayor a menor.
  3. Muestra el arreglo ordenado por consola.
  4. Ahora, revierte el orden del arreglo ya ordenado.
  5. Muestra el arreglo revertido por consola. ¿En qué orden quedaron las puntuaciones?
🏋️‍♂️ Ejercicio

Dado el arreglo de objetos: let estudiantes = [{ nombre: "Eva", edad: 20 }, { nombre: "Carlos", edad: 22 }, { nombre: "Ana", edad: 19 }];

  1. Ordena el arreglo estudiantes por edad, de menor a mayor.
  2. Muestra el resultado por consola.
  3. Ahora, ordena el arreglo estudiantes por nombre, alfabéticamente.
  4. Muestra el resultado por consola.
🚀 Extra

Investiga sobre:

  1. El método localeCompare() para strings. ¿Cómo podrías usarlo dentro de la función de comparación de sort() para ordenar strings de forma más robusta, considerando acentos o caracteres especiales según el idioma?
  2. ¿Qué es un “orden estable” (stable sort)? ¿El método sort() de JavaScript garantiza un orden estable en todos los navegadores/entornos?
  3. Los métodos más nuevos toSorted() y toReversed(). ¿Qué ventaja ofrecen sobre sort() y reverse()?

Aplanar Arreglos Anidados: flat() y flatMap()

🥞 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.

Desestructuración de Arreglos en JavaScript

🎁 Desempacando Valores

Tradicionalmente, para obtener valores de un arreglo y guardarlos en variables, haríamos algo así:

let punto = [10, 20, 30];
let x = punto[0];
let y = punto[1];
let z = punto[2];
console.log(x, y, z); // Muestra 10 20 30

La desestructuración de arreglos nos ofrece una sintaxis mucho más limpia para lograr lo mismo:

let puntoCoords = [10, 20, 30];
// Desestructuración
let [coordX, coordY, coordZ] = puntoCoords;
console.log(coordX, coordY, coordZ); // Muestra 10 20 30

Usamos corchetes [] en el lado izquierdo de la asignación (let, const, var) y colocamos los nombres de las variables que queremos crear. JavaScript asignará los elementos del arreglo puntoCoords a estas variables en orden.

✨ Características Clave

1. Declaración y Asignación Separadas

Podemos declarar las variables primero y luego desestructurar (aunque es menos común):

let nombre, apellido;
let persona = ["Ana", "García"];
[nombre, apellido] = persona;
console.log(nombre); // Muestra "Ana"
console.log(apellido); // Muestra "García"

2. Saltar Elementos

Si no necesitamos un elemento intermedio, podemos simplemente omitir su nombre usando una coma , como marcador de posición:

let colores = ["Rojo", "Verde", "Azul", "Amarillo"];
let [primerColor, , tercerColor] = colores; // Saltamos el segundo color ("Verde")
console.log(primerColor); // Muestra "Rojo"
console.log(tercerColor); // Muestra "Azul"

3. Valores por Defecto

Podemos asignar un valor por defecto a una variable en caso de que el elemento correspondiente en el arreglo no exista (sea undefined).

let configuracion = ["Modo Oscuro"];
let [tema = "Modo Claro", fontSize = 16] = configuracion;
console.log(tema); // Muestra "Modo Oscuro" (valor del arreglo)
console.log(fontSize); // Muestra 16 (valor por defecto, no había elemento en índice 1)
let otraConfig = [undefined, 12];
let [otroTema = "Claro", otroSize = 16] = otraConfig;
console.log(otroTema); // Muestra "Claro" (el elemento era undefined, se usa el defecto)
console.log(otroSize); // Muestra 12 (valor del arreglo)
INFO

💡 Los valores por defecto solo se usan si el elemento del arreglo es undefined o si no existe.

4. El Parámetro Rest (...)

Podemos usar el operador rest (...) al final de la desestructuración para agrupar todos los elementos restantes del arreglo en una nueva variable (que será un arreglo).

let numeros = [1, 2, 3, 4, 5, 6];
let [primero, segundo, ...restoDeNumeros] = numeros;
console.log(primero); // Muestra 1
console.log(segundo); // Muestra 2
console.log(restoDeNumeros); // Muestra [3, 4, 5, 6]

Esto es muy útil para separar el inicio de un arreglo del resto.

AVISO

⚠️ El parámetro rest (...) solo puede usarse como el último elemento en la desestructuración del arreglo.

5. Intercambiar Variables

La desestructuración ofrece una forma muy elegante de intercambiar los valores de dos variables sin necesidad de una variable temporal:

let a = 5;
let b = 10;
[a, b] = [b, a]; // ¡Magia!
console.log(a); // Muestra 10
console.log(b); // Muestra 5

6. Desestructuración Anidada

Podemos desestructurar arreglos que están dentro de otros arreglos:

let datosAnidados = ["Usuario1", ["Admin", "Editor"]];
let [usuario, [rol1, rol2]] = datosAnidados;
console.log(usuario); // Muestra "Usuario1"
console.log(rol1); // Muestra "Admin"
console.log(rol2); // Muestra "Editor"

☝️🤓
🏋️‍♂️ Ejercicio

Dado el arreglo: let coche = ["Toyota", "Corolla", 2022, "Rojo"];

Usa la desestructuración para extraer:

  1. La marca en una variable marca.
  2. El modelo en una variable modelo.
  3. El año en una variable año.
  4. El color en una variable color.

Muestra todas las variables por consola.

🏋️‍♂️ Ejercicio

Dado el arreglo: let competidores = ["Ana", "Luis", "Eva", "Juan", "Sara"];

Usa la desestructuración para:

  1. Extraer los tres primeros competidores en variables oro, plata, bronce.
  2. Extraer el resto de los competidores en un arreglo llamado otrosCompetidores.
  3. Asigna un valor por defecto de "Nadie" a bronce por si acaso solo hubiera 2 competidores.

Muestra todas las variables (oro, plata, bronce, otrosCompetidores) por consola.

🚀 Extra

Investiga sobre: 1. ¿Cómo funciona la desestructuración con los resultados de funciones que devuelven arreglos (ej. string.match(), Object.entries())? 2. ¿Puedes usar la desestructuración de arreglos en los parámetros de una función? 3. Compara la desestructuración de arreglos con la desestructuración de objetos. ¿Cuáles son sus similitudes y diferencias?


El Operador Spread (...) en Arreglos

✨ Expandiendo Elementos

El operador Spread (o sintaxis Spread), representado por tres puntos (...), nos permite expandir los elementos de un objeto iterable (como un arreglo o un string) en lugares donde se esperan cero o más argumentos (para llamadas a funciones) o elementos (para literales de arreglo).

Imagina que tienes una caja de herramientas (un arreglo) y quieres sacar todas las herramientas y ponerlas sobre la mesa (otro lugar que espera elementos).

let herramientas = ["Martillo", "Destornillador", "Sierra"];
// Sin Spread:
let mesa = [herramientas, "Clavos"]; // Pone la CAJA en la mesa
console.log(mesa); // Muestra [["Martillo", "Destornillador", "Sierra"], "Clavos"]
// Con Spread:
let mesaConSpread = [...herramientas, "Clavos"]; // Saca las HERRAMIENTAS y las pone
console.log(mesaConSpread); // Muestra ["Martillo", "Destornillador", "Sierra", "Clavos"]

🛠️ Usos Comunes con Arreglos

1. Copiar Arreglos (Copia Superficial)

Como vimos en el artículo sobre copias, Spread es una forma excelente y concisa de crear copias superficiales:

let original = [10, 20, 30];
let copia = [...original];
copia.push(40);
console.log(original); // Muestra [10, 20, 30]
console.log(copia); // Muestra [10, 20, 30, 40]

2. Concatenar Arreglos

Podemos combinar fácilmente dos o más arreglos en uno nuevo:

let frutas = ["Manzana", "Banana"];
let verduras = ["Zanahoria", "Lechuga"];
let otros = ["Pan"];
let listaCompra = [...frutas, ...verduras, "Leche", ...otros];
console.log(listaCompra);
// Muestra ["Manzana", "Banana", "Zanahoria", "Lechuga", "Leche", "Pan"]

Es una alternativa más moderna y legible que el método concat().

3. Expandir en Argumentos de Función

Si una función espera múltiples argumentos individuales, podemos usar Spread para pasarle los elementos de un arreglo como esos argumentos.

let numeros = [5, 15, 3];
// Math.max espera argumentos individuales: Math.max(arg1, arg2, arg3)
let maximo = Math.max(...numeros); // Equivalente a Math.max(5, 15, 3)
console.log(maximo); // Muestra 15
function sumarTres(a, b, c) {
return a + b + c;
}
let valores = [10, 20, 5];
let suma = sumarTres(...valores); // Equivalente a sumarTres(10, 20, 5)
console.log(suma); // Muestra 35
INFO

💡 Esto reemplaza en gran medida la necesidad del antiguo método Function.prototype.apply().

4. Convertir Iterables en Arreglos

Podemos usar Spread dentro de un literal de arreglo [] para convertir otros iterables (como strings o NodeList del DOM) en verdaderos arreglos.

let mensaje = "Hola";
let letrasArray = [...mensaje];
console.log(letrasArray); // Muestra ["H", "o", "l", "a"]
// Suponiendo que tenemos una NodeList del DOM
// let nodos = document.querySelectorAll('p');
// let nodosArray = [...nodos]; // Convierte la NodeList en un Array

🤔 Spread vs. Rest

Es fácil confundir el operador Spread (...) con el parámetro Rest (...) que vimos en la desestructuración y en los parámetros de función.

  • Spread (...iterable): Expande un iterable en sus elementos individuales. Se usa en literales de arreglo [...arr] o llamadas a función func(...arr).
  • Rest (...variable): Agrupa elementos restantes en un solo arreglo. Se usa en desestructuración de arreglos [a, ...resto] o en la definición de parámetros de función function miFunc(...args).
// Spread
let nums1 = [1, 2];
let nums2 = [3, 4];
let combinado = [...nums1, ...nums2]; // Expande nums1 y nums2
// Rest
function sumar(...argumentos) {
// Agrupa todos los argumentos en el arreglo 'argumentos'
return argumentos.reduce((acc, val) => acc + val, 0);
}
let [primero, ...resto] = combinado; // Agrupa 2, 3, 4 en el arreglo 'resto'

☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea dos arreglos: inicio = [1, 2] y fin = [5, 6].
  2. Usando el operador Spread, crea un tercer arreglo completo que contenga los elementos de inicio, luego los números 3 y 4, y finalmente los elementos de fin.
  3. Muestra completo por consola.
🏋️‍♂️ Ejercicio

Dado el arreglo: let palabra = ["H", "o", "l", "a"];

  1. Define una función mostrarLetras que acepte cuatro argumentos (l1, l2, l3, l4) y los muestre por consola separados por guiones (ej. “H-o-l-a”).
  2. Llama a la función mostrarLetras pasándole los elementos del arreglo palabra como argumentos individuales usando el operador Spread.
🚀 Extra

Investiga sobre:

  1. ¿Se puede usar el operador Spread con objetos literales {...obj}? ¿Para qué sirve?
  2. ¿Qué otros tipos de objetos son iterables en JavaScript además de los arreglos y strings?
  3. Compara [...iterable] con Array.from(iterable). ¿Existen diferencias sutiles en su comportamiento o casos de uso?

Métodos Estáticos del Constructor Array

🏭 Métodos del Constructor Array

Además de los métodos que llamamos en las instancias de arreglos (como miArreglo.push(), miArreglo.slice()), el propio constructor Array tiene métodos útiles adjuntos directamente a él. A estos los llamamos métodos estáticos.

1. Array.isArray(valor): Verificando si es un Arreglo

Este método es la forma más confiable para determinar si un valor dado es realmente un arreglo.

let lista = [1, 2, 3];
let texto = "Hola";
let objeto = { a: 1 };
let nada = null;
console.log(Array.isArray(lista)); // Muestra true
console.log(Array.isArray(texto)); // Muestra false
console.log(Array.isArray(objeto)); // Muestra false
console.log(Array.isArray(nada)); // Muestra false
console.log(Array.isArray(new Array())); // Muestra true
AVISO

⚠️ ¿Por qué no usar instanceof Array? Puede fallar en ciertos escenarios con múltiples contextos de JavaScript (como iframes o web workers). Array.isArray() es siempre seguro.

2. Array.from(iterable[, mapFn[, thisArg]]): Creando Arreglos desde Iterables

Ya vimos Array.from() brevemente al hablar de copias. Su función principal es crear una nueva instancia de Array a partir de:

a) Objetos iterables: Como strings, Set, Map, NodeList. b) Objetos similares a arreglos (array-like): Objetos que tienen una propiedad length y elementos indexados (como el objeto arguments en funciones no-flecha).

// Desde un string
let letras = Array.from("ABC");
console.log(letras); // Muestra ['A', 'B', 'C']
// Desde un Set
let miSet = new Set(["Uno", "Dos", "Uno"]);
let setArray = Array.from(miSet);
console.log(setArray); // Muestra ["Uno", "Dos"]
// Desde una NodeList (ejemplo conceptual)
// let parrafos = document.querySelectorAll('p');
// let parrafosArray = Array.from(parrafos);
// Desde un objeto array-like
function mostrarArgumentos() {
let argsArray = Array.from(arguments); // 'arguments' es array-like
console.log(argsArray);
}
mostrarArgumentos(10, "X", true); // Muestra [10, "X", true]

Array.from() con Función de Mapeo

Opcionalmente, Array.from() puede aceptar una función de mapeo como segundo argumento. Esta función se llama para cada elemento del iterable mientras se crea el nuevo arreglo. Es como hacer Array.from(iterable).map(mapFn), pero más eficiente.

// Crear un arreglo con números del 1 al 5
let rango = Array.from({ length: 5 }, (valorNoUsado, indice) => indice + 1);
// {length: 5} es un objeto array-like
// La función map recibe (elemento, indice)
console.log(rango); // Muestra [1, 2, 3, 4, 5]
// Crear un arreglo con los cuadrados de otro
let numerosBase = [1, 2, 3, 4];
let cuadrados = Array.from(numerosBase, (num) => num * num);
console.log(cuadrados); // Muestra [1, 4, 9, 16]
INFO

💡 Array.from() es muy útil para convertir colecciones que no son arreglos (pero se parecen o son iterables) en arreglos reales para poder usar métodos como forEach, map, filter, etc.

3. Array.of(...elementos): Creando Arreglos con Argumentos

El método Array.of() crea una nueva instancia de Array con un número variable de argumentos, sin importar el número o tipo de los argumentos.

¿Por qué existe? Principalmente para solucionar la confusión del constructor new Array() cuando se le pasa un solo número.

// Con new Array()
let arr1 = new Array(2); // Crea un arreglo con 2 empty slots: [ , ]
let arr2 = new Array(1, 2); // Crea [1, 2]
// Con Array.of()
let arrOf1 = Array.of(2); // Crea [2]
let arrOf2 = Array.of(1, 2); // Crea [1, 2]
let arrOf3 = Array.of("a", true, null); // Crea ["a", true, null]
let arrOf4 = Array.of(); // Crea []

Array.of(x) siempre crea un arreglo con x como su único elemento, a diferencia de new Array(x) que crea un arreglo vacío de longitud x.


☝️🤓
🏋️‍♂️ Ejercicio

Escribe una función llamada filtrarSoloArreglos que reciba un arreglo con valores mixtos y devuelva un nuevo arreglo conteniendo únicamente los elementos que eran arreglos en la entrada. Usa Array.isArray().

Ejemplo: filtrarSoloArreglos([1, [2, 3], "a", [], {b:1}, [4]]) debería devolver [[2, 3], [], [4]].

🏋️‍♂️ Ejercicio

Usa Array.from() para:

  1. Crear un arreglo chars a partir del string "JavaScript".
  2. Crear un arreglo dobles a partir de [5, 10, 15], donde cada número del nuevo arreglo sea el doble del original (usa la función de mapeo).
  3. Crear un arreglo indices de longitud 3 que contenga los índices [0, 1, 2] (usa un objeto {length: 3} y la función de mapeo).

Muestra los tres arreglos resultantes.

🚀 Extra

Investiga sobre: 1. ¿Qué otros métodos estáticos tiene el constructor Array (quizás menos comunes)? 2. ¿Cómo funciona exactamente un “objeto similar a arreglo” (array-like object)? ¿Qué propiedades debe tener para que Array.from() funcione con él? 3. Compara Array.of(1, 2, 3) con [1, 2, 3] y new Array(1, 2, 3). ¿Hay alguna diferencia en el resultado o el rendimiento?

Comentarios