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 arreglolet listaDeCompras = ["Leche", "Pan", "Huevos", "Manzanas"];
// Ejemplo de puntuacioneslet puntuaciones = [150, 230, 95, 410];
// Un arreglo puede contener diferentes tipos de datoslet datosVariados = ["Hola", 42, true, null, "Mundo"];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:
- 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).
- Acceder a elementos fácilmente: Podemos obtener un elemento específico de la lista usando su índice (el número que le corresponde).
- Manipular la colección: Podemos agregar, eliminar, o modificar elementos de la lista de forma sencilla usando métodos específicos para arreglos.
- 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).
“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:
- ¿Existen otros tipos de “listas” o “colecciones” en JavaScript además de los arreglos? (Pista:
Set,Map) - ¿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íolet tareasPendientes = [];
// Un arreglo de númeroslet numerosFavoritos = [7, 13, 42];
// Un arreglo de stringslet coloresPrimarios = ["Rojo", "Verde", "Azul"];
// Un arreglo mixtolet 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 elementoslet 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 5console.log(arregloVacioLargo[0]); // Muestra undefined⚠️ 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 elementoconsole.log(herramientas[0]); // Muestra "Martillo"
// Acceder al tercer elementoconsole.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 2calificaciones[2] = 9;
console.log(calificaciones[2]); // Muestra 9console.log(calificaciones); // Muestra [7, 8, 9]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 3console.log(numeros); // Muestra [10, 20, <1 empty item>, 40]console.log(numeros[2]); // Muestra undefinedconsole.log(numeros.length); // Muestra 4Esto puede llevar a errores, ¡así que ten cuidado!
☝️🤓
🏋️♂️ Ejercicio
- Crea un arreglo llamado
planetasque contenga los nombres de los planetas del sistema solar en orden desde el Sol. - Muestra por consola el nombre del tercer planeta.
- Muestra por consola el nombre del último planeta.
- Crea una variable llamada
planetaFavoritoy asígnale el valor del quinto planeta del arreglo. - Muestra la variable
planetaFavoritopor consola.
🏋️♂️ Ejercicio
- Crea un arreglo llamado
inventariocon los siguientes strings:"Manzanas","Plátanos","Naranjas". - Cambia el segundo elemento
(Plátanos) por
"Fresas". - Muestra el arreglo completo por consola para verificar el cambio.
- Intenta mostrar por consola el elemento en el índice 1
- ¿Qué resultado obtienes y por qué?
🚀 Extra
Investiga sobre:
- ¿Qué son los “empty slots” o “agujeros” (holes) en los arreglos de JavaScript y por qué es mejor evitarlos?
- ¿Puedes tener un arreglo dentro de otro arreglo? ¿Cómo accederías a un elemento del arreglo interno?
- ¿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.
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 0430✨ 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);El FinalEsta es una forma muy común de obtener el último elemento, especialmente cuando no sabemos cuántos elementos tendrá el arreglo.
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 3Esta 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 5console.log(items[2]); // Muestra undefinedCrear 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
- Crea un arreglo llamado
frutascon 5 nombres de frutas. - Muestra por consola cuántas frutas hay en el arreglo usando
length. - Usando
length, muestra por consola la última fruta del arreglo. - Ahora, cambia el
lengthdel arreglo a2. - Muestra el arreglo
frutaspor consola. ¿Qué frutas quedaron?
🏋️♂️ Ejercicio
Dado el siguiente arreglo: let valores = [10, true, "Hola", null, 50];
- ¿Cuál es el valor de
valores.length? - ¿Cuál es el índice del elemento
null? - Escribe el código para acceder al elemento
"Hola"usando la propiedadlength(sin usar directamente el índice2). - ¿Qué pasaría si hiciéramos
valores.length = 10? Describe cómo quedaría el arreglo.
🚀 Extra
Investiga sobre:
- El método
.at()para acceder a elementos. ¿Cómo usaríasmiArreglo.at(-1)y qué ventaja tiene sobremiArreglo[miArreglo.length - 1]? - ¿Cuál es la diferencia exacta entre un elemento
undefinedy un “empty slot” en un arreglo? - ¿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étodo | Operación | Devuelve |
|---|---|---|
pop() | Elimina el último elemento | El elemento eliminado |
push() | Añade elementos al final | length |
shift() | Elimina el primer elemento | El elemento eliminado |
unshift() | Añade elementos al inicio | length |
pop()
Elimina el último elemento de un arreglo y lo devuelve.
const frutas = ["🍎", "🍌", "🍊"];const ultimaFruta = frutas.pop();
console.log(frutas);console.log(ultimaFruta);['🍎', '🍌']'🍊'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);["a", "b", "c", "d"]4Para 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);["a", "b", "c", "d", "e"]5shift()
Elimina el primer elemento del arreglo y lo devuelve.
const colores = ["🟥", "🟦", "🟩"];const primerColor = colores.shift();
console.log(colores);console.log(primerColor);['🟦', '🟩']'🟥'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);['📖', '🧹', '🍳']3Para añadir más de un elemento, se deben separar por comas (,)
const tareas = ["🧹", "🍳"];const nuevaLongitud = tareas.unshift("📖", "🏋️");
console.log(tareas);console.log(nuevaLongitud);['📖', '🏋️', '🧹', '🍳']4Extraer 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:
inicio: El índice donde comienza la extracción (incluido).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 finallet 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 arreglolet copiaAnimales = animales.slice();console.log(copiaAnimales); // Muestra ["Perro", "Gato", "Conejo", "Hamster", "Pez"]
// IMPORTANTE: El arreglo original NO cambiaconsole.log(animales); // Muestra ["Perro", "Gato", "Conejo", "Hamster", "Pez"]💡 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:
inicio: El índice donde comenzar a cambiar el arreglo.cantidadAEliminar(opcional): El número de elementos a eliminar desdeinicio.elemento1, elemento2, ...(opcional): Los elementos a agregar al arreglo, comenzando desdeinicio.
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 99let 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"]🔥 ¡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"];
- Usa
slice()para crear un nuevo arregloframeworksPopularesque contenga solo “React”, “Vue” y “Angular”. - Usa
slice()para crear un nuevo arregloultimosFrameworksque contenga los últimos dos elementos del arreglocodigo. - Verifica que el arreglo
codigooriginal no haya cambiado después de las operaciones anteriores.
🏋️♂️ Ejercicio
Dado el arreglo: let tareas = ["Estudiar", "Comer", "Dormir"];
- Usa
splice()para insertar la tarea"Trabajar"después de"Estudiar". - Usa
splice()para reemplazar"Dormir"con"Jugar"y"Relajarse". - Usa
splice()para eliminar"Comer"del arreglo. - Muestra el arreglo
tareasfinal después de todas las modificaciones.
🚀 Extra
Investiga sobre:
- ¿Qué significa que
slice()crea una “copia superficial” (shallow copy)? ¿Qué implicaciones tiene si el arreglo contiene objetos u otros arreglos? - ¿Cómo podrías usar
splice()para vaciar completamente un arreglo? Compara esta forma conmiArreglo.length = 0. - Los métodos más nuevos
toSpliced(). ¿Qué problema resuelve comparado consplice()?
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 copiacopiaSuperficial[0].id = 99;
// ¡El cambio se refleja en el original también!console.log(originalAnidado[0].id); // Muestra 99console.log(copiaSuperficial[0].id); // Muestra 99⚠️ ¡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 copiacopiaProfunda[1][0].sub = 500;
// El original NO se ve afectadoconsole.log(originalProfundo[1][0].sub); // Muestra 20console.log(copiaProfunda[1][0].sub); // Muestra 500Limitaciones: 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 copiacopiaEstructurada[1].set.add(3);
// El original NO se ve afectadoconsole.log(datosOriginales[1].set); // Muestra Set(2) { 1, 2 }console.log(copiaEstructurada[1].set); // Muestra Set(3) { 1, 2, 3 }💡 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
- Crea un arreglo
miArray = [5, 10, 15]. 2. Crea una referencia llamadarefArrayasignándolemiArray. 3. Crea una copia superficial llamadacopiaArrayusando el operador Spread. 4. ModificarefArrayagregando el número 20 al final (push(20)). 5. ModificacopiaArraycambiando el primer elemento a99(copiaArray[0] = 99). 6. Muestra por consolamiArray,refArrayycopiaArray. Explica por quémiArraycambió en un caso pero no en el otro.
🏋️♂️ Ejercicio
Dado el arreglo: let datos = [{ nombre: "Alice" }, { nombre: "Bob" }];
- Crea una copia superficial
copia1usandoslice(). - Crea una copia profunda
copia2usandostructuredClone(). - Cambia el nombre del primer objeto en
copia1a “Charlie” (copia1[0].nombre = "Charlie"). - Cambia el nombre del segundo objeto en
copia2a “David” (copia2[1].nombre = "David"). - Muestra por consola el arreglo
datosoriginal. ¿Cuál de las modificaciones (CharlieoDavid) 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ón | Todos los elementos pasan el test del callback |
includes() | Existe un valor específico en el arreglo | El arreglo contiene el valor buscado |
some() | Al menos un elemento cumple la condición | Algú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);trueSi 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);falseincludes()
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);trueSi buscamos un elemento que no está presente, como ”🥭” (mango):
const frutas = ["🍎", "🍐", "🍌"];const tieneMango = frutas.includes("🥭");
console.log(tieneMango);falsesome()
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);trueSi no hay ningún número par:
const numeros = [1, 3, 5, 7];const tienePar = numeros.some((num) => num % 2 === 0);
console.log(tienePar); // falsefalseArreglos - 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étodo | Descripción | Uso principal | Devuelve |
|---|---|---|---|
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);1234map()
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);[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úsculasmezcla.sort();console.log(mezcla); // Muestra ["AVE", "Gato", "pez", "perro"]// (Las mayúsculas suelen ir antes que las minúsculas en Unicode)⚠️ ¡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
adebe ir antes queb. - Un número positivo si
adebe ir después queb. 0siaybson 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 mayornumerosParaOrdenar.sort((a, b) => a - b);console.log(numerosParaOrdenar); // Muestra [2, 5, 10, 25, 100]¿Cómo funciona a - b?
- Si
aes menor queb,a - bes negativo (ava antes). - Si
aes mayor queb,a - bes positivo (ava después). - Si son iguales,
a - bes 0 (orden relativo no cambia).
Para ordenar de mayor a menor, simplemente invertimos la resta: (a, b) => b - a.
// Ordenar de mayor a menornumerosParaOrdenar.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 }]*/🔥 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
- Crea un arreglo
puntuaciones = [88, 95, 72, 100, 81]. - Ordena el arreglo
puntuacionesde mayor a menor. - Muestra el arreglo ordenado por consola.
- Ahora, revierte el orden del arreglo ya ordenado.
- 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 }];
- Ordena el arreglo
estudiantespor edad, de menor a mayor. - Muestra el resultado por consola.
- Ahora, ordena el arreglo
estudiantespor nombre, alfabéticamente. - Muestra el resultado por consola.
🚀 Extra
Investiga sobre:
- El método
localeCompare()para strings. ¿Cómo podrías usarlo dentro de la función de comparación desort()para ordenar strings de forma más robusta, considerando acentos o caracteres especiales según el idioma? - ¿Qué es un “orden estable” (stable sort)? ¿El método
sort()de JavaScript garantiza un orden estable en todos los navegadores/entornos? - Los métodos más nuevos
toSorted()ytoReversed(). ¿Qué ventaja ofrecen sobresort()yreverse()?
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 nivelconsole.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]💡 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 pasosconsole.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);⚠️ 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]];
- Usa
flat()para crear un nuevo arreglodataAplanadaque contenga todos los números en un solo nivel. - Muestra
dataAplanadapor consola.
🏋️♂️ Ejercicio
Dado el arreglo: let items = [ { nombre: "A", tags: ["t1", "t2"] }, { nombre: "B", tags: ["t3"] }, { nombre: "C", tags: ["t2", "t4"] } ];
- Usa
flatMap()para obtener un único arreglotodosLosTagsque contenga todos los tags de todos los items, sin duplicados si es posible (pista: puedes usarSetdespués delflatMapo investigar cómo hacerlo directamente). - Muestra
todosLosTagspor consola.
🚀 Extra
Investiga sobre:
- ¿Cuál es la ganancia de rendimiento de
flatMap()comparado conmap().flat()? - ¿Cómo podrías implementar tu propia función
miFlat(arreglo, profundidad)que imite el comportamiento deflat()usando recursión o iteración? - Considera el arreglo
[1, 2, , 4, [5, , 7]]. ¿Cuál sería el resultado de aplicarflat()yflatMap(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 30La desestructuración de arreglos nos ofrece una sintaxis mucho más limpia para lograr lo mismo:
let puntoCoords = [10, 20, 30];
// Desestructuraciónlet [coordX, coordY, coordZ] = puntoCoords;
console.log(coordX, coordY, coordZ); // Muestra 10 20 30Usamos 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)💡 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 1console.log(segundo); // Muestra 2console.log(restoDeNumeros); // Muestra [3, 4, 5, 6]Esto es muy útil para separar el inicio de un arreglo del resto.
⚠️ 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 10console.log(b); // Muestra 56. 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:
- La marca en una variable
marca. - El modelo en una variable
modelo. - El año en una variable
año. - 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:
- Extraer los tres primeros competidores en variables
oro,plata,bronce. - Extraer el resto de los competidores en un arreglo llamado
otrosCompetidores. - Asigna un valor por defecto de
"Nadie"abroncepor 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 mesaconsole.log(mesa); // Muestra [["Martillo", "Destornillador", "Sierra"], "Clavos"]
// Con Spread:let mesaConSpread = [...herramientas, "Clavos"]; // Saca las HERRAMIENTAS y las poneconsole.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💡 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ónfunc(...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ónfunction miFunc(...args).
// Spreadlet nums1 = [1, 2];let nums2 = [3, 4];let combinado = [...nums1, ...nums2]; // Expande nums1 y nums2
// Restfunction 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
- Crea dos arreglos:
inicio = [1, 2]yfin = [5, 6]. - Usando el operador Spread, crea un tercer arreglo
completoque contenga los elementos deinicio, luego los números3y4, y finalmente los elementos defin. - Muestra
completopor consola.
🏋️♂️ Ejercicio
Dado el arreglo: let palabra = ["H", "o", "l", "a"];
- Define una función
mostrarLetrasque acepte cuatro argumentos (l1,l2,l3,l4) y los muestre por consola separados por guiones (ej. “H-o-l-a”). - Llama a la función
mostrarLetraspasándole los elementos del arreglopalabracomo argumentos individuales usando el operador Spread.
🚀 Extra
Investiga sobre:
- ¿Se puede usar el operador Spread con objetos literales
{...obj}? ¿Para qué sirve? - ¿Qué otros tipos de objetos son iterables en JavaScript además de los arreglos y strings?
- Compara
[...iterable]conArray.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 trueconsole.log(Array.isArray(texto)); // Muestra falseconsole.log(Array.isArray(objeto)); // Muestra falseconsole.log(Array.isArray(nada)); // Muestra falseconsole.log(Array.isArray(new Array())); // Muestra true⚠️ ¿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 stringlet letras = Array.from("ABC");console.log(letras); // Muestra ['A', 'B', 'C']
// Desde un Setlet 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-likefunction 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 5let 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 otrolet numerosBase = [1, 2, 3, 4];let cuadrados = Array.from(numerosBase, (num) => num * num);console.log(cuadrados); // Muestra [1, 4, 9, 16]💡 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:
- Crear un arreglo
charsa partir del string"JavaScript". - Crear un arreglo
doblesa partir de[5, 10, 15], donde cada número del nuevo arreglo sea el doble del original (usa la función de mapeo). - Crear un arreglo
indicesde 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?