Los objetos te permiten almacenar colecciones de datos a travs de nombres. Eso est bien.
Pero a menudo necesitamos una coleccin ordenada, donde tenemos un 1ro, un 2do, un 3er elemento y as sucesivamente. Por ejemplo, necesitamos almacenar una lista de algo: usuarios, bienes, elementos HTML, etc.
No es conveniente usar objetos aqu, porque no proveen mtodos para manejar el orden de los elementos. No podemos insertar una nueva propiedad entre los existentes. Los objetos no estn hechos para eso.
Existe una estructura llamada Array (llamada en espaol arreglo o matriz/vector) para almacenar colecciones ordenadas.
Declaracin
Hay dos sintaxis para crear un array vaco:
let arr = new Array();
let arr = [];
Casi siempre se usa la segunda. Podemos suministrar elementos iniciales entre los corchetes:
let fruits = ["Apple", "Orange", "Plum"];
Los elementos del array estn numerados comenzando desde cero.
Podemos obtener un elemento por su nmero entre corchetes:
let fruits = ["Apple", "Orange", "Plum"];
alert( fruits[0] ); // Apple
alert( fruits[1] ); // Orange
alert( fruits[2] ); // Plum
Podemos reemplazar un elemento:
fruits[2] = 'Pear'; // ahora ["Apple", "Orange", "Pear"]
o agregar uno nuevo al array:
fruits[3] = 'Lemon'; // ahora ["Apple", "Orange", "Pear", "Lemon"]
La cuenta total de elementos en el array es su longitud length:
let fruits = ["Apple", "Orange", "Plum"];
alert( fruits.length ); // 3
Tambin podemos usar alert para mostrar el array completo.
let fruits = ["Apple", "Orange", "Plum"];
alert( fruits ); // Apple,Orange,Plum
Un array puede almacenar elementos de cualquier tipo.
Por ejemplo:
// mezcla de valores
let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];
// obtener el objeto del ndice 1 y mostrar su nombre
alert( arr[1].name ); // John
// obtener la funcin del ndice 3 y ejecutarla
arr[3](); // hello
Un array, al igual que un objeto, puede tener una coma final:
let fruits = [
"Apple",
"Orange",
"Plum",
];
La coma final hace ms simple insertar y remover items, porque todas la lneas se vuelven similares.
Obtener los ltimos elementos con at
Digamos que queremos el ltimo elemento de un array.
Algunos lenguajes de programacin permiten el uso de ndices negativos para este propsito, como fruits[-1].
Sin embargo, en JavaScript esto no funcionar. El resultado ser undefined, porque el ndice entre corchetes se interpreta literalmente.
Podemos calcular explcitamente el ltimo ndice y luego acceder al elemento: fruits[fruits.length - 1].
let fruits = ["Apple", "Orange", "Plum"];
alert( fruits[fruits.length-1] ); // Plum
Un poco engorroso, no es cierto? Necesitamos escribir el nombre de la variable dos veces.
Afortunadamente, hay una sintaxis ms corta: fruits.at(-1):
let fruits = ["Apple", "Orange", "Plum"];
// es lo mismo que fruits[fruits.length-1]
alert( fruits.at(-1) ); // Plum
En otras palabras, arr.at(i):
- es exactamente lo mismo que
arr[i], sii >= 0. - para valores negativos de
i, salta hacia atrs desde el final del array.
Mtodos pop/push, shift/unshift
Una cola es uno de los usos ms comunes de un array. En ciencias de la computacin, significa una coleccin ordenada de elementos que soportan dos operaciones:
pushinserta un elemento al final.shiftobtiene el elemento del principio, avanzando la cola, y as el segundo elemento se vuelve primero.
Los arrays soportan ambas operaciones.
En la prctica los necesitamos muy a menudo. Por ejemplo, una cola de mensajes que necesitamos mostrar en pantalla.
Hay otro caso de uso para los arrays la estructura de datos llamada pila.
Ella soporta dos operaciones:
pushagrega un elemento al final.poptoma un elemento desde el final.
Entonces los elementos nuevos son agregados o tomados siempre desde el final.
Una pila es usualmente mostrada como un mazo de cartas, donde las nuevas cartas son agregadas al tope o tomadas desde el tope:
Para las pilas, la ltima introducida es la primera en ser recibida, en ingls esto es llamado principio LIFO (Last-In-First-Out, ltima en entrar primera en salir). Para las colas, tenemos FIFO (First-In-First-Out primera en entrar, primera en salir).
Los arrays en JavaScript pueden trabajar como colas o pilas. Ellos permiten agregar/quitar elementos al/del principio o al/del final.
En ciencias de la computacin, la estructura de datos que permite esto se denomina cola de doble extremo o bicola.
Mtodos que trabajan sobre el final del array:
pop-
Extrae el ltimo elemento del array y lo devuelve:
let fruits = ["Apple", "Orange", "Pear"]; alert( fruits.pop() ); // quita "Pear" y lo muestra en un alert alert( fruits ); // Apple, OrangeTanto
fruits.pop()comofruits.at(-1)devuelven el ltimo elemento del array, perofruits.pop()tambin modifica el array eliminando tal elemento. push-
Agrega el elemento al final del array:
let fruits = ["Apple", "Orange"]; fruits.push("Pear"); alert( fruits ); // Apple, Orange, PearEl llamado a
fruits.push(...)es igual afruits[fruits.length] = ....
Mtodos que trabajan con el principio del array:
shift-
Extrae el primer elemento del array y lo devuelve:
let fruits = ["Apple", "Orange", "Pear"]; alert( fruits.shift() ); // quita Apple y lo muestra en un alert alert( fruits ); // Orange, Pear unshift-
Agrega el elemento al principio del array:
let fruits = ["Orange", "Pear"]; fruits.unshift('Apple'); alert( fruits ); // Apple, Orange, Pear
Los mtodos push y unshift pueden agregar mltiples elementos de una vez:
let fruits = ["Apple"];
fruits.push("Orange", "Peach");
fruits.unshift("Pineapple", "Lemon");
// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
alert( fruits );
Interiores
Un array es una clase especial de objeto. Los corchetes usados para acceder a una propiedad arr[0] vienen de la sintaxis de objeto. Son esencialmente lo mismo que obj[key], donde arr es el objeto mientras los nmeros son usados como claves.
Ellos extienden los objetos proveyendo mtodos especiales para trabajar con colecciones ordenadas de datos y tambin la propiedad length. Pero en el corazn es an un objeto.
Recuerde, solo hay ocho tipos de datos bsicos en JavaScript (consulte el captulo Tipos de datos para obtener ms informacin). Array es un objeto y, por tanto, se comporta como un objeto.
Por ejemplo, es copiado por referencia:
let fruits = ["Banana"]
let arr = fruits; // copiado por referencia (dos variables referencian al mismo array)
alert( arr === fruits ); // true
arr.push("Pear"); // modifica el array por referencia
alert( fruits ); // Banana, Pear - ahora con 2 items
Pero lo que hace a los array realmente especiales es su representacin interna. El motor trata de almacenarlos en reas de memoria contigua, uno tras otro, justo como muestra la ilustracin en este captulo. Hay otras optimizaciones tambin para hacer que los arrays trabajen verdaderamente rpido.
Pero todo esto se puede malograr si dejamos de trabajarlos como arrays de colecciones ordenadas y comenzamos a usarlos como si fueran objetos comunes.
Por ejemplo, tcnicamente podemos hacer esto:
let fruits = []; // crea un array
fruits[99999] = 5; // asigna una propiedad con un ndice mucho mayor que su longitud
fruits.age = 25; // crea una propiedad con un nombre arbitrario
Esto es posible porque los arrays son objetos en su base. Podemos agregar cualquier propiedad en ellos.
Pero el motor ver que estamos tratndolo como un objeto comn. Las optimizaciones especficas no son aptas para tales casos y sern desechadas, y sus beneficios desaparecern.
Las formas de malograr un array:
- Agregar una propiedad no numrica como
arr.test = 5. - Generar agujeros como: agregar
arr[0]y luegoarr[1000](y nada entre ellos). - Llenar el array en orden inverso, como
arr[1000],arr[999]y as.
Piensa en los arrays como estructuras especiales para trabajar con datos ordenados. Ellos proveen mtodos especiales para ello. Los arrays estn cuidadosamente afinados dentro de los motores JavaScript para funcionar con datos ordenados contiguos, por favor salos de esa manera. Y si necesitas claves arbitrarias, hay altas chances de que en realidad necesites objetos comunes {}.
Performance
Los mtodos push/pop son rpidos, mientras que shift/unshift son lentos.
Por qu es ms rpido trabajar con el final del array que con el principio? Veamos qu pasa durante la ejecucin:
fruits.shift(); // toma 1 elemento del principio
No es suficiente tomar y eliminar el elemento con el ndice 0. Los dems elementos necesitan ser renumerados tambin.
La operacin shift debe hacer 3 cosas:
- Remover el elemento con ndice
0. - Mover todos lo elementos hacia la izquierda y renumerarlos: desde el ndice
1a0, de2a1y as sucesivamente. - Actualizar la longitud: la propiedad
length.
Cuanto ms elementos haya en el array, ms tiempo tomar moverlos, ms operaciones en memoria.
Algo similar ocurre con unshift: para agregar un elemento al principio del array, necesitamos primero mover todos los elementos hacia la derecha, incrementando sus ndices.
Y qu pasa con push/pop? Ellos no necesitan mover nada. Para extraer un elemento del final, el mtodo pop limpia el ndice y acorta length.
Las acciones para la operacin pop:
fruits.pop(); // toma 1 elemento del final
El mtodo pop no necesita mover nada, porque los dems elementos mantienen sus ndices. Es por ello que es muy rpido.
Algo similar ocurre con el mtodo push.
Bucles
Una de las formas ms viejas de iterar los items de un array es el bucle for sobre sus ndices:
let arr = ["Apple", "Orange", "Pear"];
for (let i = 0; i < arr.length; i++) {
alert( arr[i] );
}
Pero para los arrays tambin hay otra forma de bucle,for..of:
let fruits = ["Apple", "Orange", "Plum"];
// itera sobre los elementos del array
for (let fruit of fruits) {
alert( fruit );
}
for..of no da acceso al nmero del elemento en curso, solamente a su valor, pero en la mayora de los casos eso es suficiente. Y es ms corto.
Tcnicamente, y porque los arrays son objetos, es tambin posible usar for..in:
let arr = ["Apple", "Orange", "Pear"];
for (let key in arr) {
alert( arr[key] ); // Apple, Orange, Pear
}
Pero es una mala idea. Existen problemas potenciales con esto:
-
El bucle
for..initera sobre todas las propiedades, no solo las numricas.Existen objetos simil-array en el navegador y otros ambientes que parecen arrays. Esto es, tienen
lengthy propiedades indexadas, pero pueden tambin tener propiedades no numricas y mtodos que usualmente no necesitemos. Y el buclefor..inlos listar. Entonces si necesitamos trabajar con objetos simil-array, estas propiedades extras pueden volverse un problema. -
El bucle
for..inest optimizado para objetos genricos, no para arrays, y es de 10 a 100 veces ms lento. Por supuesto es an muy rpido. Una optimizacin puede que solo sea importante en cuellos de botella, pero necesitamos ser concientes de la diferencia.
En general, no deberamos usar for..in en arrays.
Acerca de length
La propiedad length automticamente se actualiza cuando se modifica el array. Para ser precisos, no es la cuenta de valores del array sino el mayor ndice ms uno.
Por ejemplo, un elemento simple con un ndice grande da una longitud grande:
let fruits = [];
fruits[123] = "Apple";
alert( fruits.length ); // 124
Nota que usualmente no usamos arrays de este modo.
Otra cosa interesante acerca de la propiedad length es que se puede sobrescribir.
Si la incrementamos manualmente, nada interesante ocurre. Pero si la decrementamos, el array se trunca. El proceso es irreversible, aqu el ejemplo:
let arr = [1, 2, 3, 4, 5];
arr.length = 2; // truncamos a 2 elementos
alert( arr ); // [1, 2]
arr.length = 5; // reponemos la longitud length
alert( arr[3] ); // undefined: el valor no se recupera
Entonces la forma ms simple de limpiar un array es: arr.length = 0;.
new Array()
Hay una sintaxis ms para crear un array:
let arr = new Array("Apple", "Pear", "etc");
Es raramente usada porque con corchetes [] es ms corto. Tambin hay una caracterstica peculiar con ella.
Si new Array es llamado con un nico argumento numrico, se crea un array sin items, pero con la longitud length dada.
Veamos cmo uno puede dispararse en el pie:
let arr = new Array(2); // Crear un array de [2]?
alert( arr[0] ); // undefined! sin elementos.
alert( arr.length ); // longitud 2
Para evitar sorpresas solemos usar corchetes, salvo que sepamos lo que estamos haciendo.
Arrays multidimensionales
Los arrays pueden tener items que a su vez sean arrays. Podemos usarlos como arrays multidimensionales, por ejemplo para almacenar matrices:
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
alert( matrix[0][1] ); // 2, el segundo valor del primer array interno
toString
Los arrays tienen su propia implementacin del mtodo toString que devuelve un lista de elementos separados por coma.
Por ejemplo:
let arr = [1, 2, 3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true
Probemos esto tambin:
alert( [] + 1 ); // "1"
alert( [1] + 1 ); // "11"
alert( [1,2] + 1 ); // "1,21"
Los arrays no tienen Symbol.toPrimitive ni un valueOf viable, ellos implementan la conversin toString solamente, as [] se vuelve una cadena vaca, [1] se vuelve "1" y [1,2] se vuelve "1,2".
Cuando el operador binario ms "+" suma algo a una cadena, lo convierte a cadena tambin, entonces lo siguiente se ve as:
alert( "" + 1 ); // "1"
alert( "1" + 1 ); // "11"
alert( "1,2" + 1 ); // "1,21"
No compares arrays con ==
Las arrays en JavaScript, a diferencia de otros lenguajes de programacin, no deben ser comparadas con el operador ==.
Este operador no tiene un tratamiento especial para arrays, trabaja con ellas como con cualquier objeto.
Recordemos las reglas:
- Dos objetos son iguales
==solo si hacen referencia al mismo objeto. - Si uno de los argumentos de
==es un objeto y el otro es un primitivo, entonces el objeto se convierte en primitivo, como se explica en el captulo Conversin de objeto a valor primitivo. - Con la excepcin de
nullyundefinedque son iguales==entre s y nada ms.
La comparacin estricta === es an ms simple, ya que no convierte tipos.
Entonces, si comparamos arrays con ==, nunca son iguales, a no ser que comparemos dos variables que hacen referencia exactamente a la misma array.
Por ejemplo:
alert( [] == [] ); // falso
alert( [0] == [0] ); // falso
Estas arrays son tcnicamente objetos diferentes. As que no son iguales. El operador == no hace comparaciones de elemento a elemento.
Comparaciones con primitivos tambin pueden dar resultados aparentemente extraos:
alert( 0 == [] ); // verdadero
alert('0' == [] ); // falso
Aqu, en ambos casos, comparamos un primitivo con un objeto array. Entonces la array [] se convierte a primitivo para el propsito de comparar y se convierte en una string vaca ''.
Luego el proceso de comparacin contina con los primitivos, como se describe en el captulo Conversiones de Tipos:
// despus de que [] se convierta en ''
alert( 0 == '' ); // verdadero, ya que '' se convierte en el nmero 0
alert('0' == '' ); // falso, sin conversin de tipos, strings diferentes
Entonces, cmo comparamos arrays?
Simple: no utilices el operador ==. En lugar, compralas elemento a elemento en un bucle o utilizando mtodos de iteracin explicados en el siguiente captulo.
Resumen
Los arrays son una clase especial de objeto, adecuados para almacenar y manejar items de datos ordenados.
La declaracin:
// corchetes (lo usual)
let arr = [item1, item2...];
// new Array (excepcionalmente raro)
let arr = new Array(item1, item2...);
El llamado a new Array(number) crea un array con la longitud dada, pero sin elementos.
- La propiedad
lengthes la longitud del array o, para ser preciso, el ltimo ndice numrico ms uno. Se autoajusta al usar los mtodos de array. - Si acortamos
lengthmanualmente, el array se trunca.
Obtener los elementos:
- Podemos obtener un elemento por su ndice, como
arr[0] - Tambin podemos usar el mtodo
at(i), que permite ndices negativos. Para valores negativos dei, cuenta hacia atrs desde el final del array. Cuandoi >= 0, funciona igual quearr[i].
Podemos usar un array como una pila deque o bicola con las siguientes operaciones:
push(...items)agregaitemsal final.pop()remueve el elemento del final y lo devuelve.shift()remueve el elemento del principio y lo devuelve.unshift(...items)agregaitemsal principio.
Para iterar sobre los elementos de un array:
for (let i=0; i<arr.length; i++)lo ms rpido, compatible con viejos navegadores.for (let item of arr)la sintaxis moderna para items solamente.for (let i in arr)nunca lo uses.
Para comparar arrays, no uses el operador == (como tampoco >, < y otros), ya que no tienen un tratamiento especial para arrays. Lo manejan como cualquier objeto y no es lo que normalmente queremos.
En su lugar puedes utilizar el bucle for..of para comparar arrays elemento a elemento.
Volveremos a los arrays y estudiaremos ms mtodos para agregar, quitar, extraer elementos y ordenar arrays en el captulo Mtodos de arrays.
Comentarios
<code>, para varias lneas envolverlas en la etiqueta<pre>, para ms de 10 lneas utilice una entorno controlado (sandbox) (plnkr, jsbin, codepen)