Articles

Cómo encontrar el id máximo en un array de objetos en JavaScript

Posted on

Recientemente estaba montando un tutorial para unos alumnos y me encontré con un problema que seguro que todos nos hemos encontrado alguna vez. Tenía este array de objetos y en cada objeto tenía un id. Quería una forma rápida y eficiente de mirar todos los ids y encontrar el id máximo.

En este artículo os guiaré a través de 4 soluciones diferentes.

  • Array.forEach
  • Array.map
  • Array.reduce
  • Math.max

Configuración de tu proyecto

Estoy usando Node para ejecutar este ejemplo pero puedes usar fácilmente JavaScript y soltar esto en una página web. Lo primero que voy a hacer es requerir el módulo assert de Node que nos da la capacidad de proporcionar un conjunto simple de pruebas de aserción.

const assert = require("assert");

Luego vas a crear un array de caracteres. Recientemente leí el libro Bad Blood así que decidí usar algunos de los personajes de ese libro. Nota al margen: Si no has leído ese libro todavía, lo recomiendo encarecidamente. Cada personaje del array va a ser un objeto que contiene un id, un nombre y un apellido.

const characters = ;

En cada solución posible se quiere escribir alguna lógica que devuelva el mayor id del array.

Array.forEach

Tu primer pensamiento podría ser iterar sobre el array usando algún tipo de bucle y por qué no, es lo que te han enseñado a hacer desde que empezaste a escribir código. Puedes empezar declarando un máximo de cero, iterar sobre cada carácter y si su id es mayor que el máximo, actualizar el máximo.

let max = 0;characters.forEach(character => { if (character.id > max) { max = character.id; }});assert(max === 444);

Mi principal objetivo cada vez que escribo código es conseguir que algo funcione primero y luego mejorarlo. Si ejecutaras este código seguro que funciona pero no me parece correcto. ¿Qué pasa si tienes mil o un millón de objetos en el array? Cada vez que empiezo a iterar sobre un array usando algún tipo de bucle para realizar algún cálculo es una enorme bandera roja para mí.

Array.map

Cada vez que aparece esa bandera roja de iterar sobre un array me pregunto inmediatamente si es algo que map/filter/reduce puede resolver. Así que si ibas a tomar ese enfoque aquí puedes empezar con el método map. El método map() crea un nuevo array con los resultados de llamar a una función proporcionada en cada elemento del array llamado. Utilizarás el método map para crear un nuevo array que contenga sólo los id’s por lo que ya no estarás trabajando con objetos. En este punto puedes usar el método de ordenación normal en el array, coger el último elemento de la lista y ese es tu id máximo.

const ids = characters.map(user => user.id);const sorted = ids.sort((a, b) => a - b);assert(sorted === 444);

Si quieres ponerte muy elegante puedes hacer todo eso en una sola sentencia.

assert( characters.map(user => user.id).sort((a, b) => a - b) === 444);

Array.reduce

El método reduce() ejecuta una función reductora (que tú proporcionas) en cada miembro del array dando como resultado un único valor de salida. Aquí vas a llamar a reducir en el array de caracteres. Definirás un acumulador que acumulará el valor de retorno de la llamada. Cada vez que se llame a la función callback devolverá un valor y lo almacenará en max. El método callback está mirando el id de los caracteres y si es mayor que max devuelve eso, si no devuelve max. Finalmente necesita establecer un valor por defecto para max y eso es lo que characters.id está haciendo.

const maxId = characters.reduce( (max, character) => (character.id > max ? character.id : max), characters.id);assert(maxId === 444);

Si te das cuenta este es un enfoque similar al que hacemos usando el método forEach, iterando sobre cada elemento y comparándolo con max. La diferencia aquí es que usar reduce es mucho más rápido. También puedes usar una combinación de reduce y la siguiente solución Math.max() si lo deseas.

El Operador Spread

Aunque me gusta el enfoque de map y reduce mucho más que iterar sobre el array todavía no me parece genial. Si investigas en Math.max descubrirás que puede tomar una lista de números o un array de números.

Esto significa que todo lo que tienes que hacer es obtener un array de id’s y puedes pasarlo a la función max(). Si estabas prestando atención ya hiciste eso antes usando map. Esto significa que puedes usar la función map para obtener un array de id’s y luego pasarlo a la función max() usando el operador spread.

assert(Math.max(...characters.map(user => user.id)) === 444);

La sintaxis de propagación permite que un iterable, como una expresión de matriz o una cadena, se expanda en lugares donde se esperan cero o más argumentos (para las llamadas a funciones) o elementos (para los literales de matriz), o que una expresión de objeto se expanda en lugares donde se esperan cero o más pares clave-valor (para literales de objeto).

Esta es para mí la solución más limpia y resulta que me gusta mucho.

Resultados de rendimiento

No pretendía convertir esto en un artículo sobre programación funcional en JavaScript pero aquí es donde aterrizamos. Hice un cronometraje básico de cada una de las funciones (usando console.time() &console.timeEnd()) y encontré los siguientes resultados.

  • prueba de iteración: 0,217ms
  • prueba de mapa: 0,191ms
  • prueba de reducción: 0,144ms
  • prueba de propagación: 0,148ms

Conclusión

Si bien el uso de reduce nos dio una ventaja de rendimiento muy ligera, prefiero usar Math.max y el operador spread aquí. Para mí simplemente se ve más limpio y es algo que me gusta escribir. Si te dieran este problema, ¿qué solución elegirías? ¿Me falta alguna? Espero que hayas disfrutado recorriendo cómo resolvería este problema y hasta la próxima…

Feliz CodificaciónDan

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *