Las matrices proporcionan muchos métodos. Para facilitar las cosas, en este capítulo se dividen en grupos.
Ya conocemos métodos que añaden y eliminan elementos desde el principio o el final:
arr.push(...items)
– agrega elementos al final,
arr.pop()
– extrae un elemento del final,
arr.shift()
– extrae un elemento desde el principio,
arr.unshift(...items)
: agrega elementos al principio.
Aquí hay algunos otros.
¿Cómo eliminar un elemento del array?
Las matrices son objetos, por lo que podemos intentar usar delete
:
let arr = ["yo", "ir", "casa"]; eliminar arreglo[1]; // eliminar "ir" alerta( arreglo[1] ); // indefinido // ahora arr = ["Yo", , "casa"]; alerta (arr.longitud); // 3
El elemento fue eliminado, pero la matriz aún tiene 3 elementos, podemos ver que arr.length == 3
.
Eso es natural, porque delete obj.key
elimina un valor mediante la key
. Es todo lo que hace. Bien para objetos. Pero para los arrays normalmente queremos que el resto de los elementos se desplacen y ocupen el lugar liberado. Esperamos tener una gama más corta ahora.
Por tanto, se deben utilizar métodos especiales.
El método arr.splice es una navaja suiza para matrices. Puede hacer de todo: insertar, quitar y reemplazar elementos.
La sintaxis es:
arr.splice(inicio[, eliminarContar, elem1, ..., elemN])
Modifica arr
comenzando desde el start
del índice: elimina los elementos deleteCount
y luego inserta elem1, ..., elemN
en su lugar. Devuelve la matriz de elementos eliminados.
Este método es fácil de entender con ejemplos.
Comencemos con la eliminación:
let arr = ["Yo", "estudiar", "JavaScript"]; arr.empalme(1, 1); // del índice 1 elimina 1 elemento alerta( llegada ); // ["Yo", "JavaScript"]
Fácil, ¿verdad? A partir del índice 1
eliminó 1
elemento.
En el siguiente ejemplo, eliminamos 3 elementos y los reemplazamos con los otros dos:
let arr = ["yo", "estudiar", "JavaScript", "derecho", "ahora"]; // elimina los 3 primeros elementos y los reemplaza por otros arr.splice(0, 3, "Vamos", "baile"); alerta(arr) // ahora ["Vamos", "baile", "derecha", "ahora"]
Aquí podemos ver que splice
devuelve la matriz de elementos eliminados:
let arr = ["yo", "estudiar", "JavaScript", "derecho", "ahora"]; // elimina los 2 primeros elementos dejar eliminado = arr.splice(0, 2); alerta (eliminada); // "yo", "estudio" <-- matriz de elementos eliminados
El método splice
también permite insertar los elementos sin necesidad de retirarlos. Para eso, necesitamos establecer deleteCount
en 0
:
let arr = ["Yo", "estudiar", "JavaScript"]; // del índice 2 // eliminar 0 // luego insertamos "complejo" y "idioma" arr.splice(2, 0, "complejo", "idioma"); alerta( llegada ); // "yo", "estudio", "complejo", "lenguaje", "JavaScript"
Índices negativos permitidos
Aquí y en otros métodos de matriz, se permiten índices negativos. Especifican la posición desde el final de la matriz, como aquí:
sea arr = [1, 2, 5]; // desde el índice -1 (a un paso del final) // eliminar 0 elementos, // luego insertamos 3 y 4 arr.splice(-1, 0, 3, 4); alerta( llegada ); // 1,2,3,4,5
El método arr.slice es mucho más simple que el arr.splice
de aspecto similar.
La sintaxis es:
arr.slice([inicio], [fin])
Devuelve una nueva matriz copiando en ella todos los elementos desde start
hasta end
del índice (sin incluir end
). Tanto start
como end
pueden ser negativos, en ese caso se asume la posición desde el final de la matriz.
Es similar al método de cadena str.slice
, pero en lugar de subcadenas, crea subarreglos.
Por ejemplo:
let arr = ["t", "e", "s", "t"]; alerta (arr.slice(1, 3)); // e,s (copiar del 1 al 3) alerta (arr.slice(-2)); // s,t (copiar desde -2 hasta el final)
También podemos llamarlo sin argumentos: arr.slice()
crea una copia de arr
. Esto se usa a menudo para obtener una copia para futuras transformaciones que no deberían afectar la matriz original.
El método arr.concat crea una nueva matriz que incluye valores de otras matrices y elementos adicionales.
La sintaxis es:
arr.concat(arg1, arg2...)
Acepta cualquier número de argumentos, ya sean matrices o valores.
El resultado es una nueva matriz que contiene elementos de arr
, luego arg1
, arg2
, etc.
Si un argumento argN
es una matriz, entonces se copian todos sus elementos. De lo contrario, se copia el argumento en sí.
Por ejemplo:
sea arr = [1, 2]; // crea una matriz a partir de: arr y [3,4] alerta( arr.concat([3, 4]) ); // 1,2,3,4 // crea una matriz a partir de: arr y [3,4] y [5,6] alerta( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6 // crea una matriz a partir de: arr y [3,4], luego suma los valores 5 y 6 alerta( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6
Normalmente, sólo copia elementos de matrices. Otros objetos, incluso si parecen matrices, se agregan como un todo:
sea arr = [1, 2]; let arrayComo = { 0: "algo", longitud: 1 }; alerta (arr.concat (arrayLike)); // 1,2,[objeto Objeto]
…Pero si un objeto similar a una matriz tiene una propiedad especial Symbol.isConcatSpreadable
, concat
lo trata como una matriz: en su lugar, se agregan sus elementos:
sea arr = [1, 2]; let arrayComo = { 0: "algo", 1: "más", [Symbol.isConcatSpreadable]: verdadero, longitud: 2 }; alerta (arr.concat (arrayLike)); // 1,2,algo,más
El método arr.forEach permite ejecutar una función para cada elemento de la matriz.
La sintaxis:
arr.forEach(función(elemento, índice, matriz) { // ... hacer algo con un elemento });
Por ejemplo, esto muestra cada elemento de la matriz:
// para cada elemento llamar a alerta ["Bilbo", "Gandalf", "Nazgul"].forEach(alerta);
Y este código es más elaborado sobre sus posiciones en la matriz de destino:
["Bilbo", "Gandalf", "Nazgul"].forEach((elemento, índice, matriz) => { alert(`${item} está en el índice ${index} en ${array}`); });
El resultado de la función (si devuelve alguno) se descarta y se ignora.
Ahora cubramos los métodos que buscan en una matriz.
Los métodos arr.indexOf y arr.includes tienen una sintaxis similar y hacen esencialmente lo mismo que sus contrapartes de cadena, pero operan con elementos en lugar de caracteres:
arr.indexOf(item, from)
: busca item
a partir del índice from
y devuelve el índice donde se encontró; de lo contrario, -1
.
arr.includes(item, from)
: busca item
a partir del índice from
y devuelve true
si lo encuentra.
Normalmente, estos métodos se utilizan con un solo argumento: el item
a buscar. Por defecto la búsqueda es desde el principio.
Por ejemplo:
let arr = [1, 0, falso]; alerta( arr.indexOf(0) ); // 1 alerta (arr.indexOf (falso)); // 2 alerta (arr.indexOf (nulo)); // -1 alerta( arr.incluye(1) ); // verdadero
Tenga en cuenta que indexOf
utiliza la igualdad estricta ===
para comparar. Entonces, si buscamos false
, encuentra exactamente false
y no el cero.
Si queremos verificar si item
existe en la matriz y no necesitamos el índice, entonces se prefiere arr.includes
.
El método arr.lastIndexOf es el mismo que indexOf
, pero busca de derecha a izquierda.
let frutas = ['Manzana', 'Naranja', 'Manzana'] alerta( frutas.indexOf('Apple') ); // 0 (primera manzana) alerta( frutas.lastIndexOf('Apple') ); // 2 (última manzana)
El método includes
maneja NaN
correctamente
Una característica menor, pero digna de mención, de includes
es que maneja correctamente NaN
, a diferencia de indexOf
:
constante arr = [NaN]; alerta (arr.indexOf (NaN)); // -1 (incorrecto, debería ser 0) alert( arr.includes(NaN) );// verdadero (correcto)
Esto se debe a que includes
se agregó a JavaScript mucho más tarde y utiliza internamente el algoritmo de comparación más actualizado.
Imaginemos que tenemos una serie de objetos. ¿Cómo encontramos un objeto con una condición específica?
Aquí el método arr.find(fn) resulta útil.
La sintaxis es:
let resultado = arr.find(función(elemento, índice, matriz) { // si se devuelve verdadero, se devuelve el elemento y se detiene la iteración // para escenario falso devuelve indefinido });
La función se llama para los elementos de la matriz, uno tras otro:
item
es el elemento.
index
es su índice.
array
es la matriz misma.
Si devuelve true
, se detiene la búsqueda y se devuelve el item
. Si no se encuentra nada, se devuelve undefined
.
Por ejemplo, tenemos una serie de usuarios, cada uno con los campos id
y name
. Busquemos el que tiene id == 1
:
dejar usuarios = [ {id: 1, nombre: "Juan"}, {id: 2, nombre: "Pete"}, {id: 3, nombre: "María"} ]; let usuario = usuarios.find(elemento => item.id == 1); alerta(nombre.usuario); // John
En la vida real, las matrices de objetos son algo común, por lo que el método find
es muy útil.
Tenga en cuenta que en el ejemplo que proporcionamos para find
la función item => item.id == 1
con un argumento. Esto es típico, rara vez se utilizan otros argumentos de esta función.
El método arr.findIndex tiene la misma sintaxis pero devuelve el índice donde se encontró el elemento en lugar del elemento en sí. Se devuelve el valor de -1
si no se encuentra nada.
El método arr.findLastIndex es como findIndex
, pero busca de derecha a izquierda, similar a lastIndexOf
.
He aquí un ejemplo:
dejar usuarios = [ {id: 1, nombre: "Juan"}, {id: 2, nombre: "Pete"}, {id: 3, nombre: "María"}, {id: 4, nombre: "Juan"} ]; // Encuentra el índice del primer Juan. alerta(usuarios.findIndex(usuario => usuario.nombre == 'John')); // 0 // Encuentra el índice del último John. alert(users.findLastIndex(usuario => usuario.nombre == 'John')); // 3
El método find
busca un único (primer) elemento que hace que la función devuelva true
.
Si puede haber muchos, podemos usar arr.filter(fn).
La sintaxis es similar a find
, pero filter
devuelve una matriz de todos los elementos coincidentes:
let resultados = arr.filter(función(elemento, índice, matriz) { // si el elemento verdadero se envía a los resultados y la iteración continúa // devuelve una matriz vacía si no se encuentra nada });
Por ejemplo:
dejar usuarios = [ {id: 1, nombre: "Juan"}, {id: 2, nombre: "Pete"}, {id: 3, nombre: "María"} ]; // devuelve una matriz de los dos primeros usuarios let someUsers = usuarios.filter(item => item.id < 3); alerta (algunosUsuarios.longitud); // 2
Pasemos a los métodos que transforman y reordenan una matriz.
El método arr.map es uno de los más útiles y utilizados con frecuencia.
Llama a la función para cada elemento de la matriz y devuelve la matriz de resultados.
La sintaxis es:
let resultado = arr.map(función(elemento, índice, matriz) { // devuelve el nuevo valor en lugar del elemento });
Por ejemplo, aquí transformamos cada elemento en su longitud:
let longitudes = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length); alerta (longitudes); // 5,7,6
La llamada a arr.sort() ordena la matriz en su lugar , cambiando el orden de sus elementos.
También devuelve la matriz ordenada, pero el valor devuelto generalmente se ignora, ya que el propio arr
se modifica.
Por ejemplo:
sea arr = [ 1, 2, 15 ]; // el método reordena el contenido de arr arr.sort(); alerta( llegada ); // 1, 15, 2
¿Notaste algo extraño en el resultado?
El orden pasó a ser 1, 15, 2
. Incorrecto. ¿Pero por qué?
Los elementos se ordenan como cadenas de forma predeterminada.
Literalmente, todos los elementos se convierten en cadenas para realizar comparaciones. Para las cadenas, se aplica el orden lexicográfico y, de hecho, "2" > "15"
.
Para utilizar nuestro propio orden de clasificación, debemos proporcionar una función como argumento de arr.sort()
.
La función debe comparar dos valores arbitrarios y devolver:
función comparar (a, b) { si (a > b) devuelve 1; // si el primer valor es mayor que el segundo si (a == b) devuelve 0; // si los valores son iguales si (a <b) devuelve -1; // si el primer valor es menor que el segundo }
Por ejemplo, para ordenar como números:
función compararNumeric(a, b) { si (a > b) devuelve 1; si (a == b) devuelve 0; si (a <b) devuelve -1; } sea arr = [ 1, 2, 15 ]; arr.sort(compararNumeric); alerta(arr); // 1, 2, 15
Ahora funciona según lo previsto.
Hagámonos a un lado y pensemos en lo que está pasando. El arr
puede ser una variedad de cualquier cosa, ¿verdad? Puede contener números, cadenas, objetos o lo que sea. Tenemos un conjunto de algunos artículos . Para ordenarlo, necesitamos una función de ordenación que sepa comparar sus elementos. El valor predeterminado es un orden de cadena.
El método arr.sort(fn)
implementa un algoritmo de clasificación genérico. No es necesario que nos importe cómo funciona internamente (un ordenamiento rápido optimizado o Timsort la mayor parte del tiempo). Recorrerá la matriz, comparará sus elementos usando la función proporcionada y los reordenará; todo lo que necesitamos es proporcionar el fn
que realiza la comparación.
Por cierto, si alguna vez queremos saber qué elementos se comparan, nada nos impide avisarles:
[1, -2, 15, 2, 0, 8]. ordenar (función (a, b) { alerta( a + " <> " + b ); devolver a-b; });
El algoritmo puede comparar un elemento con muchos otros en el proceso, pero intenta hacer la menor cantidad de comparaciones posible.
Una función de comparación puede devolver cualquier número.
En realidad, solo se requiere una función de comparación para devolver un número positivo para decir "mayor" y un número negativo para decir "menos".
Eso permite escribir funciones más cortas:
sea arr = [ 1, 2, 15 ]; arr.sort(function(a, b) { return a - b; }); alerta(arr); // 1, 2, 15
Funciones de flecha para mejor
¿Recuerdas las funciones de flecha? Podemos usarlos aquí para una clasificación más ordenada:
arr.sort( (a, b) => a - b );
Esto funciona exactamente igual que la versión más larga anterior.
Utilice localeCompare
para cadenas
¿Recuerdas el algoritmo de comparación de cadenas? Compara letras por sus códigos de forma predeterminada.
Para muchos alfabetos, es mejor usar el método str.localeCompare
para ordenar las letras correctamente, como Ö
.
Por ejemplo, clasifiquemos algunos países en alemán:
let países = ['Österreich', 'Andorra', 'Vietnam']; alerta( países.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (incorrecto) alerta( países.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (¡correcto!)
El método arr.reverse invierte el orden de los elementos en arr
.
Por ejemplo:
sea arr = [1, 2, 3, 4, 5]; arr.reverse(); alerta( llegada ); // 5,4,3,2,1
También devuelve la matriz arr
después de la inversión.
Esta es la situación de la vida real. Estamos escribiendo una aplicación de mensajería y la persona ingresa en la lista de receptores delimitada por comas: John, Pete, Mary
. Pero para nosotros una serie de nombres sería mucho más cómodo que una sola cadena. ¿Cómo conseguirlo?
El método str.split(delim) hace exactamente eso. Divide la cadena en una matriz según el delimitador dado delim
.
En el siguiente ejemplo, dividimos por una coma seguida de un espacio:
let nombres = 'Bilbo, Gandalf, Nazgul'; let arr = nombres.split(', '); para (deje el nombre de arr) { alerta(`Un mensaje para ${nombre}.`); // Un mensaje para Bilbo (y otros nombres) }
El método split
tiene un segundo argumento numérico opcional: un límite en la longitud de la matriz. Si se proporciona, los elementos adicionales se ignoran. Sin embargo, en la práctica rara vez se utiliza:
let arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2); alerta(arr); // Bilbo, Gandalf
Dividir en letras
La llamada a split(s)
con una s
vacía dividiría la cadena en una serie de letras:
let str = "prueba"; alerta( str.split('') ); // prueba
La llamada arr.join(glue) hace lo contrario para split
. Crea una cadena de arr
unidos por glue
entre ellos.
Por ejemplo:
let arr = ['Bilbo', 'Gandalf', 'Nazgul']; let str = arr.join(';'); // pega la matriz en una cadena usando ; alerta (cadena); // Bilbo;Gandalf;Nazgûl
Cuando necesitamos iterar sobre una matriz, podemos usar forEach
, for
o for..of
.
Cuando necesitamos iterar y devolver los datos de cada elemento, podemos usar map
.
Los métodos arr.reduce y arr.reduceRight también pertenecen a esa clase, pero son un poco más complejos. Se utilizan para calcular un valor único basado en la matriz.
La sintaxis es:
let valor = arr.reduce(función(acumulador, elemento, índice, matriz) { //... }, [inicial]);
La función se aplica a todos los elementos de la matriz uno tras otro y "continúa" su resultado hasta la siguiente llamada.
Argumentos:
accumulator
: es el resultado de la llamada a la función anterior, es igual initial
la primera vez (si se proporciona initial
).
item
: es el elemento de la matriz actual.
index
– es su posición.
array
: es la matriz.
A medida que se aplica la función, el resultado de la llamada a la función anterior se pasa a la siguiente como primer argumento.
Entonces, el primer argumento es esencialmente el acumulador que almacena el resultado combinado de todas las ejecuciones anteriores. Y al final, se convierte en el resultado de reduce
.
¿Suena complicado?
La forma más fácil de entender esto es con el ejemplo.
Aquí obtenemos la suma de una matriz en una línea:
sea arr = [1, 2, 3, 4, 5]; let resultado = arr.reduce((suma, actual) => suma + actual, 0); alerta(resultado); // 15
La función pasada para reduce
usa solo 2 argumentos, eso suele ser suficiente.
Veamos los detalles de lo que está pasando.
En la primera ejecución, sum
es el valor initial
(el último argumento de reduce
), es igual a 0
, y current
es el primer elemento de la matriz, es igual a 1
. Entonces el resultado de la función es 1
.
En la segunda ejecución, sum = 1
, le agregamos el segundo elemento de la matriz ( 2
) y regresamos.
En la tercera ejecución, sum = 3
y le agregamos un elemento más, y así sucesivamente…
El flujo de cálculo:
O en forma de tabla, donde cada fila representa una llamada de función en el siguiente elemento de la matriz:
sum | current | resultado | |
---|---|---|---|
la primera llamada | 0 | 1 | 1 |
la segunda llamada | 1 | 2 | 3 |
la tercera llamada | 3 | 3 | 6 |
la cuarta llamada | 6 | 4 | 10 |
la quinta llamada | 10 | 5 | 15 |
Aquí podemos ver claramente cómo el resultado de la llamada anterior se convierte en el primer argumento de la siguiente.
También podemos omitir el valor inicial:
sea arr = [1, 2, 3, 4, 5]; // eliminó el valor inicial de reducir (no 0) let resultado = arr.reduce((suma, actual) => suma + actual); alerta (resultado); // 15
El resultado es el mismo. Esto se debe a que si no hay una inicial, entonces reduce
toma el primer elemento de la matriz como valor inicial y comienza la iteración desde el segundo elemento.
La tabla de cálculo es la misma que la anterior, menos la primera fila.
Pero tal uso requiere un cuidado extremo. Si la matriz está vacía, la llamada reduce
sin valor inicial genera un error.
He aquí un ejemplo:
let arr = []; // Error: Reducción de matriz vacía sin valor inicial // si el valor inicial existiera, reducir lo devolvería para el arreglo vacío. arr.reduce((suma, actual) => suma + actual);
Por eso se recomienda especificar siempre el valor inicial.
El método arr.reduceRight hace lo mismo pero va de derecha a izquierda.
Las matrices no forman un tipo de lenguaje separado. Se basan en objetos.
Entonces typeof
no ayuda a distinguir un objeto simple de una matriz:
alerta (tipo de {}); // objeto alerta(tipo de []); // objeto (igual)
…Pero los arreglos se usan con tanta frecuencia que existe un método especial para eso: Array.isArray(valor). Devuelve true
si el value
es una matriz y false
en caso contrario.
alerta(Array.isArray({})); // FALSO alerta(Array.isArray([])); // verdadero
Casi todos los métodos de matriz que llaman a funciones, como find
, filter
, map
, con una notable excepción de sort
, aceptan un parámetro adicional opcional thisArg
.
Ese parámetro no se explica en las secciones anteriores porque rara vez se utiliza. Pero para que esté completo, tenemos que cubrirlo.
Aquí está la sintaxis completa de estos métodos:
arr.find(func, thisArg); arr.filter(func, thisArg); arr.map(func, thisArg); //... // thisArg es el último argumento opcional
El valor del parámetro thisArg
se convierte en this
para func
.
Por ejemplo, aquí usamos un método de objeto army
como filtro y thisArg
pasa el contexto:
dejar ejército = { edad mínima: 18, edad máxima: 27, puede unirse (usuario) { return edad.usuario >= esta.edadmin && edad.usuario < esta.edadmax; } }; dejar usuarios = [ {edad: 16}, {edad: 20}, {edad: 23}, {edad: 30} ]; // busca usuarios, para quienes Army.canJoin devuelve verdadero let soldados = usuarios.filter(army.canJoin, ejército); alerta(soldados.longitud); // 2 alerta(soldados[0].edad); // 20 alerta(soldados[1].edad); // 23
Si en el ejemplo anterior usamos users.filter(army.canJoin)
, entonces se llamaría army.canJoin
como una función independiente, con this=undefined
, lo que generaría un error instantáneo.
Una llamada a users.filter(army.canJoin, army)
se puede reemplazar con users.filter(user => army.canJoin(user))
, que hace lo mismo. Este último se utiliza con más frecuencia, ya que es un poco más fácil de entender para la mayoría de las personas.
Una hoja de referencia de métodos de matriz:
Para agregar/eliminar elementos:
push(...items)
– agrega elementos al final,
pop()
– extrae un elemento del final,
shift()
– extrae un elemento desde el principio,
unshift(...items)
: agrega elementos al principio.
splice(pos, deleteCount, ...items)
: en el índice pos
elimina elementos deleteCount
e inserta items
.
slice(start, end)
: crea una nueva matriz, copia elementos desde start
del índice hasta end
(no incluidos) en ella.
concat(...items)
: devuelve una nueva matriz: copia todos los miembros de la actual y le agrega items
. Si alguno de items
es una matriz, se toman sus elementos.
Para buscar entre elementos:
indexOf/lastIndexOf(item, pos)
: busca item
a partir de la posición pos
y devuelve el índice o -1
si no se encuentra.
includes(value)
: devuelve true
si la matriz tiene value
; de lo contrario, false
.
find/filter(func)
: filtra elementos a través de la función, devuelve primero/todos los valores que hacen que devuelva true
.
findIndex
es como find
, pero devuelve el índice en lugar de un valor.
Para iterar sobre elementos:
forEach(func)
: llama func
para cada elemento, no devuelve nada.
Para transformar la matriz:
map(func)
: crea una nueva matriz a partir de los resultados de llamar func
para cada elemento.
sort(func)
: ordena la matriz en el lugar y luego la devuelve.
reverse()
: invierte la matriz en su lugar y luego la devuelve.
split/join
: convierte una cadena en una matriz y viceversa.
reduce/reduceRight(func, initial)
: calcula un valor único sobre la matriz llamando func
para cada elemento y pasando un resultado intermedio entre las llamadas.
Además:
Array.isArray(value)
comprueba que value
sea una matriz; si es así, devuelve true
; de lo contrario, false
.
Tenga en cuenta que los métodos sort
, reverse
y splice
modifican la matriz misma.
Estos métodos son los más utilizados, cubren el 99% de los casos de uso. Pero hay algunos más:
arr.some(fn)/arr.every(fn) comprueba la matriz.
La función fn
se llama en cada elemento de la matriz de manera similar a map
. Si alguno o todos los resultados son true
, devuelve true
; en caso contrario, false
.
Estos métodos se comportan algo así como ||
y operadores &&
: si fn
devuelve un valor verdadero, arr.some()
inmediatamente devuelve true
y deja de iterar sobre el resto de elementos; si fn
devuelve un valor falso, arr.every()
devuelve inmediatamente false
y también deja de iterar sobre el resto de los elementos.
Podemos usar every
para comparar matrices:
función matricesEqual(arr1, arr2) { return arr1.length === arr2.length && arr1.every((valor, índice) => valor === arr2[índice]); } alerta( arraysEqual([1, 2], [1, 2])); // verdadero
arr.fill(valor, inicio, fin): llena la matriz con value
repetido desde start
hasta end
del índice.
arr.copyWithin(destino, inicio, fin): copia sus elementos desde la posición start
hasta la posición end
en sí mismo , en la posición target
(sobrescribe los existentes).
arr.flat(profundidad)/arr.flatMap(fn) crea una nueva matriz plana a partir de una matriz multidimensional.
Para obtener la lista completa, consulte el manual.
A primera vista puede parecer que existen tantos métodos que resultan bastante difíciles de recordar. Pero en realidad, eso es mucho más fácil.
Mire la hoja de trucos solo para estar al tanto de ellos. Luego resuelva las tareas de este capítulo para practicar, de modo que tenga experiencia con métodos de matriz.
Luego, cuando necesites hacer algo con una matriz y no sepas cómo, ven aquí, mira la hoja de trucos y encuentra el método correcto. Los ejemplos te ayudarán a escribirlo correctamente. Pronto recordarás automáticamente los métodos, sin ningún esfuerzo específico por tu parte.
importancia: 5
Escriba la función camelize(str)
que cambia palabras separadas por guiones como "my-short-string" por "myShortString" en formato camel.
Es decir: elimina todos los guiones, cada palabra después del guión pasa a estar en mayúsculas.
Ejemplos:
camelize("color de fondo") == 'Color de fondo'; camelize("lista-estilo-imagen") == 'listStyleImage'; camelize("-webkit-transition") == 'WebkitTransition';
Sugerencia de PD: use split
para dividir la cadena en una matriz, transfórmela y join
unirla.
Abra una caja de arena con pruebas.
función camelizar (cadena) { cadena de retorno .split('-') // divide 'mi-palabra-larga' en una matriz ['mi', 'larga', 'palabra'] .mapa( // pone en mayúscula las primeras letras de todos los elementos de la matriz excepto la primera // convierte ['mi', 'larga', 'palabra'] en ['mi', 'larga', 'palabra'] (palabra, índice) => índice == 0 ? palabra: palabra[0].toUpperCase() + palabra.slice(1) ) .unirse(''); // une ['my', 'Long', 'Word'] en 'myLongWord' }
Abra la solución con pruebas en un sandbox.
importancia: 4
Escriba una función filterRange(arr, a, b)
que obtenga una matriz arr
, busque elementos con valores mayores o iguales a a
e inferiores o iguales a b
y devuelva un resultado como una matriz.
La función no debe modificar la matriz. Debería devolver la nueva matriz.
Por ejemplo:
sea arr = [5, 3, 8, 1]; dejar filtrado = filterRange(arr, 1, 4); alerta (filtrada); // 3,1 (valores coincidentes) alerta( llegada ); // 5,3,8,1 (no modificado)
Abra una caja de arena con pruebas.
función rango de filtro(arr, a, b) { // se agregaron corchetes alrededor de la expresión para una mejor legibilidad return arr.filter(item => (a <= item && item <= b)); } sea arr = [5, 3, 8, 1]; dejar filtrado = filterRange(arr, 1, 4); alerta (filtrada); // 3,1 (valores coincidentes) alerta( llegada ); // 5,3,8,1 (no modificado)
Abra la solución con pruebas en un sandbox.
importancia: 4
Escriba una función filterRangeInPlace(arr, a, b)
que obtenga una matriz arr
y elimine todos los valores excepto aquellos que están entre a
y b
. La prueba es: a ≤ arr[i] ≤ b
.
La función solo debe modificar la matriz. No debería devolver nada.
Por ejemplo:
sea arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // eliminó los números excepto del 1 al 4 alerta( llegada ); // [3, 1]
Abra una caja de arena con pruebas.
función filterRangeInPlace(arr, a, b) { for (sea i = 0; i <arr.length; i++) { let val = arr[i]; // eliminar si está fuera del intervalo si (val < a || val > b) { arr.empalme(i, 1); i--; } } } sea arr = [5, 3, 8, 1]; filterRangeInPlace(arr, 1, 4); // eliminó los números excepto del 1 al 4 alerta( llegada ); // [3, 1]
Abra la solución con pruebas en un sandbox.
importancia: 4
sea arr = [5, 2, 1, -10, 8]; // ... tu código para ordenarlo en orden decreciente alerta( llegada ); // 8, 5, 2, 1, -10
sea arr = [5, 2, 1, -10, 8]; arr.sort((a, b) => b - a); alerta( llegada );
importancia: 5
Tenemos una serie de cadenas arr
. Nos gustaría tener una copia ordenada, pero arr
sin modificaciones.
Cree una función copySorted(arr)
que devuelva dicha copia.
let arr = ["HTML", "JavaScript", "CSS"]; dejar ordenado = copiarOrdenado(arr); alerta (ordenado); // CSS, HTML, JavaScript alerta( llegada ); // HTML, JavaScript, CSS (sin cambios)
Podemos usar slice()
para hacer una copia y ejecutar la clasificación:
función copiarOrdenado(arr) { devolver arr.slice().sort(); } let arr = ["HTML", "JavaScript", "CSS"]; dejar ordenado = copiarOrdenado(arr); alerta (ordenado); alerta( llegada );
importancia: 5
Cree una Calculator
de función constructora que cree objetos de calculadora "extendibles".
La tarea consta de dos partes.
Primero, implemente el método calculate(str)
que toma una cadena como "1 + 2"
en el formato “operador NÚMERO NÚMERO” (delimitado por espacios) y devuelve el resultado. Debe entender más +
y menos -
.
Ejemplo de uso:
let calc = nueva Calculadora; alerta( calc.calcular("3 + 7") ); // 10
Luego agregue el método addMethod(name, func)
que le enseña a la calculadora una nueva operación. Toma el name
del operador y la función de dos argumentos func(a,b)
que lo implementa.
Por ejemplo, agreguemos la multiplicación *
, la división /
y la potencia **
:
let powerCalc = nueva Calculadora; powerCalc.addMethod("*", (a, b) => a * b); powerCalc.addMethod("/", (a, b) => a/b); powerCalc.addMethod("**", (a, b) => a ** b); let resultado = powerCalc.calculate("2 ** 3"); alerta (resultado); // 8
No hay paréntesis ni expresiones complejas en esta tarea.
Los números y el operador están delimitados exactamente con un espacio.
Puede haber errores en el manejo si desea agregarlo.
Abra una caja de arena con pruebas.
Tenga en cuenta cómo se almacenan los métodos. Simplemente se agregan a la propiedad this.methods
.
Todas las pruebas y conversiones numéricas se realizan mediante el método calculate
. En el futuro, es posible que se amplíe para admitir expresiones más complejas.
función Calculadora() { estos.métodos = { "-": (a, b) => a - b, "+": (a, b) => a + b }; this.calcular = función (cadena) { dejar dividir = str.split(' '), a = +dividir[0], op = dividir[1], b = +dividir[2]; if (!this.methods[op] || esNaN(a) || esNaN(b)) { devolver NaN; } devolver this.methods[op](a, b); }; this.addMethod = función (nombre, función) { this.methods[nombre] = func; }; }
Abra la solución con pruebas en un sandbox.
importancia: 5
Tiene una variedad de objetos user
, cada uno tiene user.name
. Escribe el código que lo convierte en una matriz de nombres.
Por ejemplo:
let john = { nombre: "John", edad: 25 }; let pete = { nombre: "Pete", edad: 30 }; let mary = { nombre: "Mary", edad: 28 }; dejar usuarios = [john, pete, mary]; let nombres = /* ... tu código */ alerta (nombres); // Juan, Pete, María
let john = { nombre: "John", edad: 25 }; let pete = { nombre: "Pete", edad: 30 }; let mary = { nombre: "Mary", edad: 28 }; dejar usuarios = [john, pete, mary]; let nombres = usuarios.map(elemento => elemento.nombre); alerta (nombres); // Juan, Pete, María
importancia: 5
Tiene una variedad de objetos user
, cada uno tiene name
, surname
e id
.
Escriba el código para crear otra matriz a partir de él, de objetos con id
y fullName
, donde fullName
se genera a partir de name
y surname
.
Por ejemplo:
let john = { nombre: "John", apellido: "Smith", id: 1 }; let pete = { nombre: "Pete", apellido: "Hunt", id: 2 }; let mary = { nombre: "Mary", apellido: "Clave", id: 3 }; dejar usuarios = [john, pete, mary]; let usersMapped = /* ... tu código ... */ /* usuariosMapeados = [ { nombre completo: "John Smith", id: 1 }, { nombre completo: "Pete Hunt", id: 2 }, { nombre completo: "Mary Key", id: 3 } ] */ alerta (usuariosMapped[0].id) // 1 alerta( usuariosMapped[0].nombre completo ) // John Smith
Entonces, en realidad necesitas asignar una matriz de objetos a otra. Intente usar =>
aquí. Hay un pequeño problema.
let john = { nombre: "John", apellido: "Smith", id: 1 }; let pete = { nombre: "Pete", apellido: "Hunt", id: 2 }; let mary = { nombre: "Mary", apellido: "Clave", id: 3 }; dejar usuarios = [john, pete, mary]; dejar usuariosMapped = usuarios.map(usuario => ({ nombre completo: `${nombre.usuario} ${nombre.usuario}`, identificación: usuario.id })); /* usuariosMapeados = [ { nombre completo: "John Smith", id: 1 }, { nombre completo: "Pete Hunt", id: 2 }, { nombre completo: "Mary Key", id: 3 } ] */ alerta (usuariosMapped[0].id); // 1 alerta (usuariosMapped[0].nombre completo); // Juan Smith
Tenga en cuenta que en las funciones de flecha necesitamos usar corchetes adicionales.
No podemos escribir así:
dejar usuariosMapped = usuarios.map(usuario => { nombre completo: `${nombre.usuario} ${nombre.usuario}`, identificación: usuario.id });
Como recordamos, existen dos funciones de flecha: sin value => expr
y con value => {...}
.
Aquí JavaScript trataría {
como el inicio del cuerpo de la función, no como el inicio del objeto. La solución es encerrarlos entre corchetes "normales":
dejar usuariosMapped = usuarios.map(usuario => ({ nombre completo: `${nombre.usuario} ${nombre.usuario}`, identificación: usuario.id }));
Ahora bien.
importancia: 5
Escribe la función sortByAge(users)
que obtiene una matriz de objetos con la propiedad age
y los ordena por age
.
Por ejemplo:
let john = { nombre: "John", edad: 25 }; let pete = { nombre: "Pete", edad: 30 }; let mary = { nombre: "Mary", edad: 28 }; let arr = [ pete, john, mary ]; ordenarPorEdad(arr); // ahora: [juan, maría, pete] alerta(arr[0].nombre); // John alerta(arr[1].nombre); // María alerta(arr[2].nombre); // Pete
función ordenarPorEdad(arr) { arr.sort((a, b) => a.edad - b.edad); } let john = { nombre: "John", edad: 25 }; let pete = { nombre: "Pete", edad: 30 }; let mary = { nombre: "Mary", edad: 28 }; let arr = [ pete, john, mary ]; ordenarPorEdad(arr); // ahora ordenado es: [john, mary, pete] alerta(arr[0].nombre); // John alerta(arr[1].nombre); // María alerta(arr[2].nombre); // Pete
importancia: 3
Escriba la función shuffle(array)
que baraja (reordena aleatoriamente) elementos de la matriz.
Varias ejecuciones de shuffle
pueden dar lugar a diferentes órdenes de elementos. Por ejemplo:
sea arr = [1, 2, 3]; barajar(arr); // arreglo = [3, 2, 1] barajar(arr); // arreglo = [2, 1, 3] barajar(arr); // arreglo = [3, 1, 2] //...
Todos los órdenes de elementos deben tener la misma probabilidad. Por ejemplo, [1,2,3]
se puede reordenar como [1,2,3]
o [1,3,2]
o [3,1,2]
, etc., con la misma probabilidad en cada caso.
La solución sencilla podría ser:
función aleatoria (matriz) { array.sort(() => Math.random() - 0.5); } sea arr = [1, 2, 3]; barajar(arr); alerta(arr);
Eso funciona de alguna manera, porque Math.random() - 0.5
es un número aleatorio que puede ser positivo o negativo, por lo que la función de clasificación reordena los elementos aleatoriamente.
Pero como la función de clasificación no debe usarse de esta manera, no todas las permutaciones tienen la misma probabilidad.
Por ejemplo, considere el siguiente código. Se ejecuta shuffle
1000000 veces y cuenta las apariciones de todos los resultados posibles:
función aleatoria (matriz) { array.sort(() => Math.random() - 0.5); } // recuentos de apariciones para todas las permutaciones posibles dejar contar = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; para (sea i = 0; i < 1000000; i++) { let matriz = [1, 2, 3]; barajar (matriz); contar[array.join('')]++; } // muestra el recuento de todas las permutaciones posibles for (dejar teclear el conteo) { alerta(`${clave}: ${cuenta[clave]}`); }
Un resultado de ejemplo (depende del motor JS):
123: 250706 132: 124425 213: 249618 231: 124880 312: 125148 321: 125223
Podemos ver claramente el sesgo: 123
y 213
aparecen con mucha más frecuencia que otros.
El resultado del código puede variar entre los motores JavaScript, pero ya podemos ver que el enfoque no es confiable.
¿Por qué no funciona? En términos generales, sort
es una "caja negra": arrojamos una matriz y una función de comparación y esperamos que se ordene la matriz. Pero debido a la aleatoriedad total de la comparación, la caja negra se vuelve loca, y cómo exactamente se vuelve loco depende de la implementación concreta que difiere entre los motores.
Hay otras buenas formas de hacer la tarea. Por ejemplo, hay un gran algoritmo llamado Fisher-Yates Shuffle. La idea es caminar por la matriz en el orden inverso e intercambiar cada elemento con uno aleatorio antes:
function shuffle (array) { para (dejar i = array.length-1; i> 0; i--) { Sea j = math.floor (math.random () * (i + 1)); // índice aleatorio de 0 a i // matriz de elementos de intercambio [i] y matriz [j] // Utilizamos la sintaxis de "asignación de destructación" para lograr eso // Encontrarás más detalles sobre esa sintaxis en capítulos posteriores // Lo mismo se puede escribir como: // Sea t = array [i]; matriz [i] = array [j]; matriz [j] = t [array [i], array [j]] = [array [j], matriz [i]]; } }
Vamos a probarlo de la misma manera:
function shuffle (array) { para (dejar i = array.length-1; i> 0; i--) { Sea j = math.floor (math.random () * (i + 1)); [array [i], array [j]] = [array [j], matriz [i]]; } } // recuentos de apariciones para todas las permutaciones posibles Let Count = { '123': 0, '132': 0, '213': 0, '231': 0, '321': 0, '312': 0 }; para (dejar i = 0; i <1000000; i ++) { LET ARRAY = [1, 2, 3]; Shuffle (matriz); contar [array.join ('')] ++; } // Mostrar recuentos de todas las permutaciones posibles para (dejar que la clave en el recuento) { alert (`$ {key}: $ {count [key]}`); }
La salida de ejemplo:
123: 166693 132: 166647 213: 166628 231: 167517 312: 166199 321: 166316
Se ve bien ahora: todas las permutaciones aparecen con la misma probabilidad.
Además, en cuanto al rendimiento, el algoritmo Fisher-Yates es mucho mejor, no hay una "clasificación".
importancia: 4
Escriba la función getAverageAge(users)
que obtiene una variedad de objetos con age
de la propiedad y devuelve la edad promedio.
La fórmula para el promedio es (age1 + age2 + ... + ageN) / N
.
Por ejemplo:
Sea John = {nombre: "John", edad: 25}; Let Pete = {nombre: "Pete", edad: 30}; Let Mary = {nombre: "María", edad: 29}; Sea arr = [John, Pete, Mary]; alerta (getAverageagee (arr)); // (25 + 30 + 29) / 3 = 28
función getAvergegeEage (usuarios) { return ussers.reduce ((previo, usuario) => anterior + user.age, 0) / users.length; } Sea John = {nombre: "John", edad: 25}; Let Pete = {nombre: "Pete", edad: 30}; Let Mary = {nombre: "María", edad: 29}; Sea arr = [John, Pete, Mary]; alerta (getAvergegeAge (arr)); // 28
importancia: 4
Sea arr
una matriz.
Cree una función unique(arr)
que debería devolver una matriz con elementos únicos de arr
.
Por ejemplo:
función única (arr) { /* tu código */ } Let Strings = ["liebre", "krishna", "liebre", "krishna", "Krishna", "Krishna", "Liebre", "Liebre", ":-O" ]; alerta (único (cadenas)); // Liebre, Krishna, :-O
Abra una caja de arena con pruebas.
Caminemos los artículos de la matriz:
Para cada artículo verificaremos si la matriz resultante ya tiene ese elemento.
Si es así, ignore, de lo contrario agregar a los resultados.
función única (arr) { Sea resultado = []; para (dejar str of arr) { if (! resultado.includes (str)) { resultado.push (str); } } resultado de devolución; } Let Strings = ["liebre", "krishna", "liebre", "krishna", "Krishna", "Krishna", "Liebre", "Liebre", ":-O" ]; alerta (único (cadenas)); // Liebre, Krishna, :-O
El código funciona, pero hay un posible problema de rendimiento.
El resultado del método result.includes(str)
camina internamente el result
de la matriz y compara cada elemento con str
para encontrar la coincidencia.
Entonces, si hay 100
elementos en result
y nadie coincide con str
, entonces caminará todo el result
y hará exactamente 100
comparaciones. Y si result
es grande, como 10000
, entonces habría 10000
comparaciones.
Eso no es un problema por sí mismo, porque los motores JavaScript son muy rápidos, por lo que Walk 10000
Array es una cuestión de microsegundos.
Pero hacemos tal prueba para cada elemento de arr
, en el bucle for
.
Entonces, si arr.length
es 10000
, tendremos algo así como 10000*10000
= 100 millones de comparaciones. Eso es mucho.
Entonces, la solución solo es buena para matrices pequeñas.
Además en el mapa del capítulo y el conjunto, veremos cómo optimizarlo.
Abra la solución con pruebas en un sandbox.
importancia: 4
Digamos que recibimos una variedad de usuarios en la forma {id:..., name:..., age:... }
.
Cree una función groupById(arr)
que crea un objeto a partir de él, con id
como clave, y elementos de matriz como valores.
Por ejemplo:
Dejar usuarios = [ {id: 'John', nombre: "John Smith", edad: 20}, {id: 'ann', nombre: "Ann Smith", edad: 24}, {id: 'Pete', nombre: "Pete Peterson", edad: 31}, ]; dejar ussersByID = GroupById (usuarios); /* // Después de la llamada deberíamos tener: UsersByID = { John: {id: 'John', nombre: "John Smith", edad: 20}, Ann: {id: 'Ann', nombre: "Ann Smith", edad: 24}, Pete: {id: 'Pete', nombre: "Pete Peterson", Edad: 31}, } */
Dicha función es realmente útil cuando se trabaja con datos del servidor.
En esta tarea suponemos que id
es única. Puede que no haya dos elementos de matriz con la misma id
.
Utilice el método Array .reduce
en la solución.
Abra una caja de arena con pruebas.
function groupById (array) { return array.reduce ((obj, valor) => { obj [valor.id] = valor; regresar obj; }, {}) }
Abra la solución con pruebas en un sandbox.