Skip to content
This page has been auto-translated and may contain errors.View in English

Arreglos

docs.scrimba.com

Tienes un carrito de compras, una lista de puntuaciones, los nombres de todos en un chat grupal. Una variable almacena un valor, así que guardar una lista de esta manera significa un montón de nombres separados: price1, price2, price3, sin forma clara de recorrerlos o preguntar cuántos hay. Un arreglo es la solución. Almacena una lista ordenada de valores bajo un solo nombre y conoce su propia longitud, así que puedes agregar a él, leer cualquier elemento y recorrer todo en pocas líneas.

Crear y leer un arreglo

Un arreglo es una lista ordenada de valores. Lo escribes con corchetes, con los valores separados por comas:

js
const prices = [4, 9, 2, 7];

Esa es una variable, prices, que almacena cuatro números. Para leer un valor individual usas su posición, llamada su índice, en corchetes. La trampa que atrapa a casi todos al principio: la cuenta comienza en cero, no en uno.

js
const names = ["María", "Carlos", "Yuki"];
console.log(names[0]); // María
console.log(names[1]); // Carlos

Para preguntar cuántos elementos tiene un arreglo, usa .length:

js
console.log(names.length); // 3

Como la cuenta comienza en cero, el último elemento está en length - 1, no en length. Así que el último nombre aquí es names[2], y names[3] estaría fuera del final.

Un arreglo es una colección ordenada de valores bajo un nombre. Lo creas con un literal de corchetes y lees cualquier elemento por su índice, su posición contada desde cero:

js
const prices = [4, 9, 2, 7];
console.log(prices[0]); // 4
console.log(prices[2]); // 2

La indexación basada en cero es la fuente de la mayoría de los primeros errores de fuera por uno, así que vale la pena arreglarlo en tu cabeza ahora: el primer elemento está en el índice 0, y el último está en length - 1.

js
console.log(prices.length); // 4
console.log(prices[prices.length - 1]); // 7, el último elemento

Leer un índice que no existe no genera un error. prices[10] te da undefined, que es la forma de JavaScript de decir "no hay nada aquí". Ese resultado silencioso es conveniente y ocasionalmente una trampa, porque un error de tipografía en un índice te da undefined en lugar de un fallo ruidoso.

Un arreglo es una colección ordenada direccionada por índice creada con un literal de corchetes. Los elementos se leen por índice contado desde cero, y .length reporta el conteo:

js
const prices = [4, 9, 2, 7];
console.log(prices[0]); // 4
console.log(prices.length); // 4
console.log(prices[prices.length - 1]); // 7, el último elemento

Dos propiedades merecen ser nombradas con precisión. Primero, .length no es un tamaño fijo que declaraste de antemano; sigue el índice más alto en uso y se actualiza mientras agregas y removes elementos. Segundo, una lectura fuera de rango devuelve undefined en lugar de lanzar un error, así que un error de fuera por uno aparece más tarde como undefined fluyendo a través de tu lógica, no como una excepción en el punto del error. Cuando quieres que el fallo sea ruidoso, protege el índice contra length tú mismo.

Los arreglos pueden contener tipos de valor mixtos ([1, "dos", true]), pero una base de código que funciona casi siempre mantiene un arreglo para un tipo de cosa, porque cada pieza de código que lee el arreglo entonces sabe qué está obteniendo. La consistencia aquí es una decisión de legibilidad, no una regla del lenguaje.

JunoCrear y leer un arreglo Un arreglo es una lista de valores en corchetes bajo un nombre. Lee un elemento por su posición con names[0], y recuerda que la cuenta comienza en cero, así que el primer elemento es [0], no [1]. Ese cero me confundió durante una semana, así que verifica cuando tu lista esté fuera por uno. Usa .length para contar los elementos, y el último vive en length - 1.
JunoCrear y leer un arreglo Un arreglo contiene una lista ordenada bajo un nombre, y lees elementos por índice contado desde cero. El último elemento está en length - 1, que es donde vienen la mayoría de los primeros errores de fuera por uno. Leer más allá del final devuelve undefined en lugar de un error, así que un índice mal escrito va silencioso en lugar de ruidoso. Ten eso en mente cuando un valor aparezca como undefined sin razón aparente.
JunoCrear y leer un arreglo Literal de corchetes, índice basado en cero, y .length que sigue el índice más alto en uso en lugar de un tamaño que declaraste. Una lectura fuera de rango devuelve undefined en lugar de lanzar un error, así que un error de fuera por uno viaja río abajo antes de morder. Protege el índice contra length tú mismo cuando quieras que ese fallo sea ruidoso. Y mantén un arreglo para un tipo de cosa, no porque el lenguaje lo fuerce, sino porque cada lector del arreglo entonces sabe qué contiene.

Cambiar un arreglo

Los arreglos se hacen para cambiar. Los dos a los que recurres más agregan o removes en el final: push agrega un elemento, pop remueve el último.

js
const cart = ["manzanas", "pan"];
cart.push("leche");
console.log(cart); // ["manzanas", "pan", "leche"]
cart.pop();
console.log(cart); // ["manzanas", "pan"]

El par correspondiente funciona en el inicio: unshift agrega al frente, shift remueve el primer elemento.

js
cart.unshift("huevos");
console.log(cart); // ["huevos", "manzanas", "pan"]

También puedes reemplazar un elemento directamente asignándolo a su índice:

js
cart[0] = "plátanos";
console.log(cart); // ["plátanos", "manzanas", "pan"]

Para encontrar algo, includes te dice sí o no, e indexOf te dice dónde está (o -1 si no está):

js
console.log(cart.includes("manzanas")); // true
console.log(cart.indexOf("pan")); // 2

Una cosa que vale la pena saber temprano: un arreglo const aún puede cambiar. const te detiene de reasignar el nombre cart a un arreglo completamente nuevo, pero la lista a la que apunta aún está abierta para push, pop y asignación de índice.

Los arreglos están construidos para ser modificados en lugar. Dos pares de métodos hacen la mayoría del trabajo, uno en cada final:

js
const cart = ["manzanas", "pan"];
cart.push("leche"); // agregar al final
cart.pop(); // remover del final
cart.unshift("huevos"); // agregar al inicio
cart.shift(); // remover del inicio

push y pop son el par barato y común; los usas constantemente. shift y unshift tocan el frente, lo que significa que cada otro elemento tiene que moverse hacia arriba o hacia abajo una ranura, así que en un arreglo grande hacen más trabajo. Prefiere el final cuando el orden es tuyo para elegir.

Puedes sobrescribir cualquier posición asignándola a su índice, y puedes encontrar elementos de dos maneras:

js
const names = ["María", "Carlos", "Yuki"];
names[1] = "Priya";
console.log(names.indexOf("Yuki")); // 2, o -1 si está ausente
console.log(names.includes("María")); // true

Recurre a includes cuando solo te importa si algo está presente, e indexOf cuando necesites la posición también. Ambos comparan con ===, así que indexOf("2") no encontrará el número 2.

Un arreglo const sigue siendo completamente mutable: const bloquea el nombre, no los contenidos. No puedes apuntar cart a un nuevo arreglo, pero puedes hacer push en el que sostiene todo el día.

Los arreglos son mutables, y los métodos principales cambian el arreglo en lugar en lugar de devolver uno fresco. push/pop operan en el final, shift/unshift en el frente:

js
const cart = ["manzanas", "pan"];
cart.push("leche"); // devuelve la nueva longitud, muta cart
const removed = cart.pop(); // devuelve el elemento removido, muta cart

El final y el frente no son iguales en costo. push y pop tocan una sola ranura. shift y unshift reindexan cada elemento restante, que es trabajo lineal en la longitud del arreglo, así que un bucle caliente que continuamente hace shift en un arreglo grande está haciendo silenciosamente mucho más de lo que parece. Cuando el orden está bajo tu control, mantén el movimiento en el final.

Asignar a un índice escribe en lugar, y buscar se divide por necesidad: includes para presencia, indexOf para la posición (con -1 para ausente). Ambos usan igualdad estricta (===), así que el tipo importa:

js
const ids = [1, 2, 3];
console.log(ids.indexOf("2")); // -1, la cadena no coincide con el número
console.log(ids.includes(2)); // true

El punto de const vale la pena declarar exactamente, porque es el primer lugar donde el modelo de referencia aparece: const vincula el nombre a un arreglo y prohíbe revivirlo, pero no dice nada sobre los contenidos del arreglo. cart.push(...) muta el objeto al que el nombre apunta, que es una operación diferente a cart = [...]. Esa distinción es el tema completo de la próxima sección.

JunoCambiar un arreglopush y pop agregan y removen al final; unshift y shift hacen lo mismo al inicio. Asigna a un índice como cart[0] = "plátanos" para reemplazar un elemento, y usa includes o indexOf para encontrar uno. La parte que me sorprendió: un arreglo const aún puede cambiar, porque const solo te detiene de intercambiar el arreglo completo por uno nuevo.
JunoCambiar un arreglo Dos pares cubren la mayoría de ediciones: push/pop al final, shift/unshift al inicio. El par del frente mueve cada otro elemento, así que favorece el final cuando el orden es tuyo. includes verifica presencia, indexOf da la posición, y ambos coinciden con ===, así que "2" no encontrará el número 2. Y const bloquea el nombre, nunca los contenidos, así que puedes hacer push en un arreglo const todo el día.
JunoCambiar un arreglo Estos métodos mutan en lugar: push/pop tocan una ranura, shift/unshift reindexan el arreglo completo, así que un bucle que sigue haciendo shift en un arreglo grande está haciendo trabajo lineal cada vez. includes e indexOf ambos usan ===, así que un índice de cadena no coincidirá con un número. Y const solo prohíbe revincular el nombre; cart.push() mutando el arreglo es una cosa diferente de cart = []. Esa brecha es exactamente la historia de referencia que viene a continuación.

Recorrer un arreglo

Una vez que tienes una lista, generalmente quieres hacer algo con cada elemento. La forma más clara de leer un arreglo es for...of, que te da un elemento a la vez:

js
const prices = [4, 9, 2, 7];
for (const price of prices) {
  console.log(price);
}
// 4
// 9
// 2
// 7

No administras un índice o un contador. for...of te da cada valor en orden, y lo nombras (price) como quieras. Si ya has conocido bucles, este es el más amable para arreglos.

También está forEach, que ejecuta una pequeña función una vez para cada elemento:

js
const names = ["María", "Carlos", "Yuki"];
names.forEach(function (name) {
  console.log("Hola " + name);
});

Ambos recorren el arreglo completo. Recurre a for...of cuando quieras leer limpiamente, y conocerás forEach y sus primos de nuevo cuando aprendas a transformar arreglos.

El estándar moderno para leer un arreglo es for...of. Te da cada valor directamente, sin índice para administrar y sin posibilidad de un error de fuera por uno:

js
const prices = [4, 9, 2, 7];
let total = 0;
for (const price of prices) {
  total = total + price;
}
console.log(total); // 22

Usa for...of cuando quieras los valores en orden y código limpio. Si también necesitas el índice (digamos, para numerar una lista), el bucle for de conteo clásico o el segundo argumento de forEach te lo da.

forEach ejecuta una devolución de llamada, una función que le pasas, una vez por elemento:

js
const names = ["María", "Carlos", "Yuki"];
names.forEach((name) => {
  console.log("Hola " + name);
});

forEach se lee bien para un efecto secundario por elemento, como registrar. Lo que no puede hacer es salir con break, así que cuando necesites detener a mitad de camino, usa for...of. Construir un nuevo arreglo a partir de uno viejo es un trabajo diferente con sus propias herramientas, cubierto en métodos de arreglo.

Para una lectura simple, for...of es la opción idiomática: itera valores directamente, elude aritmética de índice y soporta break y continue:

js
const prices = [4, 9, 2, 7];
let total = 0;
for (const price of prices) {
  if (price > 8) continue;
  total = total + price;
}
console.log(total); // 13, omitió el 9

forEach toma una devolución de llamada por elemento y se lee limpiamente para efectos secundarios, pero viene con dos restricciones que vale la pena conocer antes de estandarizarte en ella. No puede break, así que siempre se ejecuta hasta el final, e ignora un valor devuelto, así que es la herramienta incorrecta cuando estás tratando de producir un resultado. Para eso, existen los métodos transformadores (map, filter, reduce), y métodos de arreglo es donde pertenecen.

js
const names = ["María", "Carlos", "Yuki"];
names.forEach((name, index) => {
  console.log(index + ": " + name);
});

El segundo argumento de la devolución de llamada es el índice, que es cómo recuperas la posición sin un contador manual. La regla general: for...of cuando necesites flujo de control o estés acumulando en una variable externa, forEach cuando un efecto secundario por elemento se lee más claramente, y un método transformador cuando quieras un nuevo arreglo de vuelta en lugar de una mutación o un registro.

JunoRecorrer un arreglofor...of es la forma limpia de recorrer un arreglo: te da cada elemento en orden y nunca tocas un índice. forEach ejecuta una pequeña función una vez por elemento, que también es útil. Ambos van a través de la lista completa, así que elige for...of cuando quieras leer directamente. Verás más herramientas de arreglo una vez que llegues a transformarlos.
JunoRecorrer un arreglofor...of es el estándar para leer un arreglo: valores en orden, sin índice para administrar, y break funciona cuando necesitas detener temprano. forEach ejecuta una devolución de llamada por elemento y se lee bien para efectos secundarios como registrar, pero no puede break, así que siempre se ejecuta hasta el final. Construir un nuevo arreglo a partir de uno viejo es un trabajo separado con sus propios métodos. Cuando tengas duda, for...of.
JunoRecorrer un arreglofor...of itera valores directamente y soporta break y continue, así que gana cuando necesites flujo de control o estés acumulando en una variable externa. forEach se lee limpio para un efecto secundario por elemento pero no puede romper e ignora valores devueltos, lo que lo hace la herramienta incorrecta para producir un resultado. Cuando quieras un nuevo arreglo de vuelta, recurre a los métodos transformadores en lugar de un bucle. Su segundo argumento de devolución de llamada te da el índice si necesitas posición.

Los arreglos se sostienen por referencia

Aquí hay un comportamiento que sorprende a la gente, así que vale la pena verlo temprano. Cuando copias una variable de arreglo en otro nombre, no obtienes un segundo arreglo. Ambos nombres apuntan al mismo.

js
const original = [1, 2, 3];
const copy = original;
copy.push(4);
console.log(original); // [1, 2, 3, 4]

Hacer push a copy cambió original, porque solo había un arreglo. El = le dio al segundo nombre la misma lista, no una copia fresca de ella. Esto es lo que la gente significa cuando dice que los arreglos se sostienen por referencia: la variable sostiene un puntero a la lista, no la lista en sí.

Para obtener una copia real y separada, extiende el arreglo en uno nuevo con ...:

js
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3], sin cambios

Un número o una cadena se sostienen por valor: copiar la variable copia el valor. Un arreglo es diferente. Se sostiene por referencia, así que copiar la variable copia el puntero, y ambos nombres terminan apuntando al mismo arreglo.

js
const original = [1, 2, 3];
const shared = original;
shared.push(4);
console.log(original); // [1, 2, 3, 4], el mismo arreglo

Esto es el mismo hecho detrás de la nota de const anterior: un arreglo es un objeto, y una variable sostiene una referencia a ese objeto, no el objeto en sí. Mutar a través de cualquier nombre muta el arreglo único que comparten.

Cuando quieres una copia independiente, extiéndela en un nuevo arreglo. El ... copia cada elemento:

js
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3], separado

Esta es una copia superficial: el nuevo arreglo es su propia lista, pero si los elementos fueran en sí mismos arreglos, ambas copias aún compartirían esos arreglos internos. Con números y cadenas simples, superficial es todo lo que necesitas.

Las primitivas (números, cadenas, booleanos) se sostienen por valor; los arreglos son objetos y se sostienen por referencia. Una variable que "contiene" un arreglo en realidad sostiene una referencia a él, así que la asignación copia la referencia, y cada nombre apuntando a ese arreglo ve cada mutación:

js
const original = [1, 2, 3];
const alias = original;
alias.push(4);
console.log(original); // [1, 2, 3, 4]
console.log(original === alias); // true, mismo arreglo

Esto es por qué const y mutabilidad son preocupaciones separadas: const fija la referencia, la mutación cambia lo que la referencia apunta. También es el argumento para preferir inmutabilidad, produciendo un nuevo arreglo en lugar de cambiar uno en su lugar. El estado mutable compartido significa que una función que muta un arreglo que recibió silenciosamente lo cambia para cada otro tenedor de esa referencia, que es una clase de error que es difícil de rastrear. Devolver un nuevo arreglo mantiene los datos de cada llamador como propios.

El operador de extensión hace una copia superficial: un nuevo arreglo exterior con los mismos elementos copiados.

js
const original = [1, 2, 3];
const copy = [...original];
console.log(copy === original); // false, arreglos diferentes

Superficial es la palabra que importa. El nuevo arreglo es independiente, pero los arreglos anidados dentro de él aún se comparten por referencia, así que una estructura profunda necesita más que una extensión. Con arreglos de primitivas, que es todo lo que tienes hasta ahora, una extensión es una copia completa.

En cuanto a elegir la estructura en absoluto: un arreglo es la herramienta correcta cuando tienes una lista ordenada de cosas similares y la posición o conteo importa, una cola de precios, una secuencia de nombres. Cuando en cambio quieres buscar valores por una clave significativa en lugar de una posición, eso es para lo que es un objeto, y una función es donde mucho de este trabajo de arreglo termina viviendo.

JunoLos arreglos se sostienen por referencia Copiar una variable de arreglo con = no hace un segundo arreglo; ambos nombres apuntan al mismo, así que un cambio a través de cualquiera aparece en ambos. Este me atrapó más de una vez. Cuando quieras una copia real separada, extiéndela con [...original], que construye un arreglo fresco que puedes cambiar por tu cuenta.
JunoLos arreglos se sostienen por referencia Los números y cadenas se copian por valor; los arreglos se copian por referencia, así que const b = a te da dos nombres para un arreglo, y mutar a través de cualquiera afecta a ambos. Esa es la misma razón por la que un arreglo const aún es mutable. Usa [...original] para una copia independiente, teniendo en cuenta que es superficial, que es exactamente lo que quieres para arreglos de números y cadenas.
JunoLos arreglos se sostienen por referencia Los arreglos son objetos sostenidos por referencia, así que la asignación copia el puntero y cada nombre ve cada mutación. Eso divide const (fija la referencia) de mutabilidad (cambia el objetivo), y es el caso para preferir un nuevo arreglo sobre un cambio en su lugar: una función que muta un arreglo recibido lo cambia para todos sosteniendo esa referencia. La extensión hace una copia superficial, independiente en el nivel superior pero compartiendo cualquier arreglo anidado. Recurre a un arreglo cuando el orden y el conteo importan, y a un objeto cuando quieres buscar cosas por clave.

Hacia dónde van los arreglos desde aquí

Un arreglo te da una lista ordenada bajo un nombre: créalo con [], léelo por índice desde cero, cámbialo con push y pop, y recórrelo con for...of. La idea única que debes llevar más allá de este capítulo es que un arreglo se sostiene por referencia, así que copiar la variable comparte la lista en lugar de duplicarla.

El siguiente paso natural es convertir un arreglo en otro, filtrar una lista, transformar cada elemento, sumarlo todo, sin escribir el bucle a mano. Eso son métodos de arreglo. Cuando necesites buscar valores por un nombre en lugar de una posición, objetos son la estructura para eso.