¿Cómo evitar los ciclos en JavaScript?


Un sencillo problema que necesitamos resolver casi el 100% de las veces que programamos es la de realizar una operación sobre cada uno de los elementos en una colección. Considera el siguiente snippet:

var suma = 0;
var coleccion = [1,2,3,4,5];

for(var i = 0; i < coleccion.length; i++){
  suma += coleccion[i];
}

Aquí se realizó la operación suma a todos los elementos de la colección y se guardó en la variable suma el resultado final.

Imagina realizar esto en tu app para cada caso que se presente. Lo más lógico sería refactorizar el problema y crear funciones genéricas para resolverlo:

var reducir = function( operacion , coleccion , inicial ){
  var resultado = inicial;
  for (var i = 0; i < coleccion.length; i++){
    resultado = operación( resultado , collecion[i] );
  }
  return resultado;
};

La función reducir intenta resolver este problema. Con ella implementada solo debes crear una función que defina la operación a realizar y proporcionar un valor inicial como tercer parámetro:

function suma( viejo , nuevo ){
      return viejo + nuevo;
};
var col  = [1,2,3,4,5,6];
var suma = reducir( suma , col , 0 ); // 21

Ahora es posible utilizar la misma función reducir para realizar otras operaciones. Aunque la operación concatenar sobre un arreglo de Strings se resuelve fácilmente con un join, utilizando reducir quedaría de la siguiente manera:

function concatena( v , n ){
      return v.concat(n);
};

var abcdario = ["a", "b", "c", "d"];
var unido    = reducir( concatena , abcdario , "" );
// abcd

Todo esto está bien, pero en el fondo implementé una función que itera sobre la colección con un ciclo. ¿Habrá una mejor forma de hacer lo anterior?

Sí, JavaScript una vez más al rescate.

Bienvenido, Array.prototype.reduce

Según el sitio MDN (Mozilla Developer Network):

Array.prototype.reduce aplica una función simultáneamente a dos valores de un array (de izquierda a derecha) para reducirlo a
un único valor.

La función tiene la misma estructura que nuestra primitiva función reducir, por lo que aplicándola al primer caso bastan estas líneas:

var numeros = [1,2,3,4];
var resultado = numeros.reduce( suma , 0); // 10

Observa que suma es la función que implementamos al principio. Por si fuera poco, la función concatena sirve para muchas más cosas que el trabajo con Strings. Utilizándola junto con reduce podremos combinar varios arreglos en uno solo:

var conjunto = [ [1,2] , [3,4] , [5,6] , [6,7] ];
var plano    = conjunto.reduce( concatenar , [] );

console.log(plano); // [1,2,3,4,5,6,7]

Mejorando la experiencia con EcmaScript 2015

La última especificación de JavaScript, EcmaScript 2015 (también conocida como ES6) incluye muchísimas mejoras en el lenguaje, una de ellas es la notación de flechas gordas (fat arrows) para declarar funciones.

En los ejemplos anteriores, declaré siempre las funciones suma y concatenar aparte, para que la llamada a la función reduce se vea limpia y concisa.

No obstante, te puedes ahorrar este paso utilizando una función anónima:

conjunto.reduce( function(viejo, nuevo){
      return viejo.contat(nuevo);
} , "");

Utilizando la nueva notación del lenguaje, lo anterior se reduce sustancialmente:

conjunto.reduce((v , n) => { return v.concat(n); } , "");

La notación es muy parecida a la sintaxis en CoffeeScript, solo que mantiene su carácter nativo.

Aunque la especificación ES6 ya está terminada, hay que esperar un tiempo a que los navegadores adopten las nuevas características del lenguaje. No obstante, para probar lo anterior puedes utilizar un transpiler como Babel.js o Traceur.

Conclusiones

Con esta técnica puedes decirle adiós a los bucles para resolver el patrón de reducir un arreglo a un solo valor en tus aplicaciones. Con ello no necesitas preocuparte por la lógica que implica iterar sobre la colección, dígase crear variables de índice (la famosa i), referenciarlas (colección[i]) etc.

Si necesitas utilizar el índice dentro de tu función de operación (la que se le pasa por parámetro a reduce) , solo ponlo como tercer parámetro de dicha función.

coleccion.reduce(function(viejo, nuevo, indice){...}));

Este JSbin puede ayudarte a practicar lo explicado. Espero te sirvan estos tips y lo apliques en tu trabajo diario.

Happy Scripting!