En nuestro día a día tenemos que manipular frecuentemente objetos, Strings, Arrays, etc… En esta entrada vamos a echar un vistazo a algunas funciones para Arrays en Javascript y vamos a ver cómo nos pueden ayudar a dejar nuestro código más limpio y un poco más «entendible» para nuestros compañeros.
Como ya conoceréis, para casi todos los casos en los que tengáis que recorrer un Array, tenemos varias alternativas disponibles. Por ejemplo, si queremos buscar un elemento dentro de un Array podemos usar un bucle while o un for:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
let personas = [ { nombre: 'nombre1', edad: 28 }, { nombre: 'nombre2', edad: 30 }, { nombre: 'nombre3', edad: 32 } ]; // Ejemplo con While let i = 0; let personaBuscada = null; while (personaBuscada === null) { if (personas[i].edad === 28) { personaBuscada = personas[i]; } i++; } // El mismo ejemplo con For let personaBuscada = null; for (var i = 0; i < personas.length; i++) { if (personas[i].edad === 28) { personaBuscada = personas[i]; break; } } |
Dejando de lado cuestiones de rendimiento, que en la mayoría de casos no son significativas, la principal diferencia está en la legibilidad del código.
Los dos recorren un array hasta que encuentran una persona con 28 años de edad y después salen del bucle. Si no se nos ocurre ningún motivo para utilizar uno u otro, tenemos que ponernos en el lugar de la persona que vaya a leer el programa en el futuro.
Cuando encontramos un bucle while sabemos que va a recorrer el Array hasta que se cumpla una o varias condiciones, mientras que si encontramos un for, lo normal es que recorra todos los elementos que haya desde el inicio hasta el final (en este caso el inicio es 0 y el fin es la logitud del array, pero no tiene por qué ser así). Con lo que nuestro compañero va a entender mejor nuestra intención de buscar un elemento concreto si utilizamos un bucle while.
Cuanto más simple mejor
Los problemas pueden ser simples o complejos, pero nuestro código tiene que ser siempre lo más simple posible para no convertirse en parte del problema en el futuro.
Podríamos utilizar siempre el mismo tipo de bucle y nuestro código funcionaría igual, pero a medida que el programa evolucione y se complique, aparecerán nuevas funciones y bucles enrevesados y necesitamos mantener el código lo más legible y ordenado que podamos.
Pensad que aproximadamente el 90% del tiempo que se dedique a nuestro proyecto será para mantenerlo, así que podemos mantenerlo simple («keep it simple, stupid» o KISS) o esperar a que nuestros compañeros empiecen a maldecir cuando revisen nuestro código.
Vamos a ver algunas de estas funciones :
forEach
La primera de estas funciones la utilizaremos cuando queramos hacer algo con cada elemento del array.
Es útil cuando queremos recorrer todos los elementos y hacer cualquier operación con cada uno de ellos por separado. Por ejemplo, queremos mostrar todos los elementos por la consola:
1 2 3 4 5 6 7 |
let personas = ['nombre1', 'nombre2', 'nombre3']; personas.forEach((persona) => { console.log(persona); }) |
find
Queremos buscar un elemento y recogerlo en una variable.
Si quisieramos obtener un array con todos los elementos que cumplan esta condición, deberíamos utilizar filter.
Un ejemplo con find, queremos buscar la primera persona que tenga 30 años o más:
1 2 3 4 5 6 7 8 9 10 11 12 |
let personas = [ { nombre: 'nombre1', edad: 28 }, { nombre: 'nombre2', edad: 30 }, { nombre: 'nombre3', edad: 32 } ]; let personaEncontrada = personas.find((persona) => { return persona.edad >= 30; }); // personaEncontrada es { nombre: 'nombre2', edad: 30 } |
filter
Queremos un array filtrando elementos de otro array.
Por ejemplo, queremos buscar todas las personas que tengan 30 años o más:
1 2 3 4 5 6 7 8 9 10 11 12 |
let personas = [ { nombre: 'nombre1', edad: 28 }, { nombre: 'nombre2', edad: 30 }, { nombre: 'nombre3', edad: 32 } ]; let personaEncontrada = personas.find((persona) => { return persona.edad >= 30; }); // personaEncontrada es { nombre: 'nombre2', edad: 30 } |
some
Queremos comprobar que al menos un elemento cumple la condición. Devuelve true o false dependiendo de si ha encontrado el elemento o no.
Comprobamos si hay alguna persona mayor de 40 años :
1 2 3 4 5 6 7 8 9 10 11 12 |
let personas = [ { nombre: 'nombre1', edad: 28 }, { nombre: 'nombre2', edad: 30 }, { nombre: 'nombre3', edad: 32 } ]; let dentroDelRango = personas.some((persona) => { return persona.edad > 40; }); // dentroDelRango es false |
Mismo ejemplo para comprobar si hay al menos una persona menor de 29 años:
1 2 3 4 5 6 |
let dentroDelRango = personas.some((persona) => { return persona.edad < 29; }); // dentroDelRango es true |
every
Queremos comprobar que todos los elementos cumplen con las condiciones que queramos. Si encuentra un elemento que NO cumple con la condición que queremos, sale del bucle. Devuelve true si cumplen la condición y false en caso contrario.
Comprobamos si todas las personas son mayores de 30 años :
1 2 3 4 5 6 7 8 9 10 11 12 |
let personas = [ { nombre: 'nombre1', edad: 28 }, { nombre: 'nombre2', edad: 30 }, { nombre: 'nombre3', edad: 32 } ]; var dentroDelRango = personas.every((persona) => { return persona.edad > 30; }); // dentroDelRango es false |
Comprobamos que todos son menores de 40:
1 2 3 4 5 6 |
let dentroDelRango = personas.every((persona) => { return persona.edad < 40; }); // dentroDelRango es true |
map
Queremos crear un nuevo array aplicando una función que transforme todos los elemento del array original y manteniendo el original intacto.
Imaginad que queremos quitar la palabra ‘nombre’ en la propiedad nombre de cada persona :
1 2 3 4 5 6 7 8 |
let personas = ['nombre1', 'nombre2', 'nombre3']; let ids = personas.map((persona) => { return persona.nombre.replace('nombre', '') }); // ids es ['1', '2', '3'] |
reduce
Esta función puede resultar un poco más difícil de entender, pero es realmente útil.
Con ella, vamos a acumular el array en un solo elemento. Esta es la estructura
1 2 3 |
arrayOrigen.reduce(funcionCallback, valorInicial); |
En la funcionCallback es donde metemos la lógica para acumular los valores y valorInicial es el valor inicial del total.
Por ejemplo, supongamos que tenemos un array de números y queremos acumularlos en un solo valor (una suma normal, con valor inicial 0):
1 2 3 4 5 6 7 8 |
let numeros = [299, 100, 350]; const suma = numeros.reduce((total, numero, indice, array)=> { return total + numero; }, 0); // suma es 749 |
En cada vuelta la función callback de reduce recibe 4 parámetros Total es el valor acumulado hasta ahora, numero es el elemento del array que estamos tratando en esta vuelta, indice es la posición de ese elemento en el array y por último el array original. Los dos primeros son imprescindibles y el resto opcionales.
En este ejemplo, en la primera vuelta:
total: 0, numero: 299
Segunda vuelta:
total: 299, numero: 100
Última vuelta:
total: 399, numero: 350
Y la última vuelta devolverá 399+350 que es el resultado final 749
Si no ponemos valor inicial, la primera vuelta cogerá el primer elemento del array como total:
Primera vuelta sin valor inicial:
total: 299, numero: 100
Última vuelta:
total: 399, numero: 350
En este caso ahorramos una vuelta, pero en los casos en los que queramos acumular el resultado en un nuevo array, el valor inicial es obligatorio.
También podríamos calcular la media de estos números devolviendo en la última vuelta el valor total dividido entre la longitud del array:
1 2 3 4 5 6 7 8 9 10 11 |
let numeros = [299, 100, 350]; const media = numeros.reduce((total, numero, indice, array)=> { total += numero; if (indice === array.length-1) { return total/array.length } return total; }, 0); // media es 249.66666... |
También podemos crear un nuevo array modificando el original y filtrando a la vez. Como si aplicásemos a la vez las funciones map y filter. Ejemplo, queremos hacer un descuento del 30% en los productos que tengan 100 unidades o menos :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let productos = [ { nombre: 'prod1', precio: 299, unidades: 1000 }, { nombre: 'prod2', precio: 100, unidades: 34 }, { nombre: 'prod3', precio: 1200, unidades: 134 }, { nombre: 'prod4', precio: 770, unidades: 83 }, { nombre: 'prod5', precio: 350, unidades: 46 } ]; let productosPromocionados = productos.reduce((arrayAcumulador, producto) => { if (producto.unidades <= 100) { producto.precio = producto.precio*0.7; arrayAcumulador.push(producto); } return arrayAcumulador; }, []); // productosPromocionados contiene solo los productos a los que hemos aplicado el descuento con el precio cambiado // CUIDADO: si os fijáis, hemos modificado propiedades en los objetos originales, con lo que el array original también estará modificado. Si queremos mantener el array original intacto, tenemos que copiar el objeto original antes de modificarlo y añadirlo al arrayAcumulador. Quedaría así: let productosPromocionados = productos.reduce((arrayAcumulador, producto) => { if (producto.unidades <= 100) { let copiaProducto = JSON.parse(JSON.stringify(producto)); copiaProducto.precio = copiaProducto.precio*0.7; arrayAcumulador.push(copiaProducto); } return arrayAcumulador; }, []); |
Nos será útil si tenemos que filtrar y modificar a la vez, ya que hacemos las dos operaciones recorriendo una sola vez toda la colección y lo agradeceremos cuando la colección ser muy grande.
Si tuviésemos que hacer solo una de las operaciones es mejor utilizar filter o map por separado, recordad que queremos que se entiendan nuestras intenciones.
Evidentemente, hay otras funciones para Arrays en Javascript, pero no tampoco podemos abarcar todas de una tacada. Si empezamos a utilizar estas poco a poco, al final las utilizaremos de forma automática y podremos enfocarnos en otros puntos de mejora.
Espero que os sea útil
Hacer Comentario