Las matrices de JavaScript son una forma increíblemente flexible de modelar colecciones utilizando técnicas de programación funcional. Este artículo le presenta el uso de herramientas como forEach(), map() y reduce() para matrices de estilo funcional. Matrices de JavaScript tradicionales Las matrices de JavaScript pueden contener tipos heterogéneos, cambiar el tamaño sobre la marcha e insertar o eliminar elementos fácilmente. Los métodos tradicionales como cortar, empalmar y empujar/pop hacen esto operando en la matriz misma, modificando la colección de una manera “destructiva”: // Crear una matriz con tipos heterogéneos: let myArray = [10, «hello», true, { name: «Alice» }]; // Agregar a una matriz y cambiar el tamaño sobre la marcha: myArray.push(42); // Extraer elementos sin modificar la matriz original: let extract = myArray.slice(1, 3); Programación funcional con matrices Aunque las matrices de JavaScript son muy capaces desde el primer momento, el paradigma funcional mejora la claridad y la facilidad de mantenimiento del código de matriz. En general, la programación funcional busca utilizar funciones como operadores que pueden pasarse a matrices. Esto permite operar sobre la matriz como el cabezal de una cinta, en lugar de los tradicionales bucles imperativos que describen en detalle lo que va a ocurrir. Veamos algunos ejemplos de cómo trabajar con matrices en el paradigma funcional.forEach()Array.forEach( ) es nuestro primer ejemplo. Esto le permite pasar una función que realiza operaciones arbitrarias en los elementos de forma iterativa. Es una alternativa común al bucle for tradicional: myArray.forEach((elemento) => { console.log(«mi elemento es: » + elemento); }) En este ejemplo, solo estamos enviando cada elemento a la consola. El equivalente en un bucle for sería: for (sea i = 0; i < myArray.length; i++) { console.log("my element is: " + myArray[i]); } You’ll notice that there are fewer moving parts in the functional version. In particular, we eliminate the iterator (i), which is an extraneous variable used to express the logic of the mechanics, rather than a part of the actual intention. Note that I'm not suggesting for loops have no place; they are sometimes the right tool, but often, forEach() is a cleaner approach.Functional programming as a philosophy promotes “immutability.” That means simply that it likes to avoid modifying variables. Instead, functional programming prefers to take an existing variable and pass it through a “pipeline” (a function or functions) that transforms it into a new variable, leaving the original as-is. forEach is often used in this way, but it’s also often used “destructively,” as shown here: const numbers = [1, 2, 3, 4, 5]; numbers.forEach(function(number, index) { if (number % 2 === 0) { // Check for even numbers numbers.splice(index, 1); // Remove even numbers from the array } }); This example might not be considered the purest form of functional programming, but it utilizes key functional characteristics such as “first-order functions.” When we refer to a first-order function, we mean that we are using a function like any other reference, in this case, by passing it in as an argument. The long and the short of that story is that functions can act as portable bundles of functionality that are passed around to do jobs in predictable ways.Note, too, that there are still many cases where an old-fashioned for loop is the best approach. For example, when iterating by a number other than 1, iterating backward, and when handling complex scenarios requiring multiple iterators.Array.map()Functions that are non-destructive and avoid any other “side-effects” are said to be “pure functions.” We can use forEach in this way, but the Array.map() function is specifically designed for this purpose. It does not operate on the array itself, but instead runs the function operator and returns the result as a new array: const bands = [ { name: "Led Zeppelin", year: 1968 }, { name: "Pink Floyd", year: 1965 }, { name: "Queen", year: 1970 }, { name: "The Clash", year: 1976 }, { name: "The Ramones", year: 1974 }, { name: "R.E.M.", year: 1980 }, ]; const bandNames = bands.map(band => { return nombre.banda; }); // bandNames es una matriz que tiene solo los nombres de las bandas de cadenas Array.map() es un mecanismo muy poderoso para transformar matrices. Le brinda la posibilidad de hacer casi cualquier cosa con una matriz de forma limpia. En particular, evita la complejidad al cambiar la matriz original, donde otro código en otros lugares podría depender de ella de maneras desconocidas o inesperadas. Por otro lado, es importante tener en cuenta que Array.map() siempre hace una copia, lo que tiene implicaciones en el rendimiento. No querrás utilizarlo en matrices muy grandes. A veces, las consideraciones de memoria dictan que se utilice otro enfoque. La forma en que funciona esto es que todo lo que devuelva la función proporcionada se mantendrá en la nueva matriz. Entonces, podríamos usar la versión que regresa automáticamente de una función: const bandNames = bands.map(band => band.name) Este enfoque puede ser mucho más limpio para funciones cortas. Array.filter()Array.map() genera una matriz con la misma longitud que la fuente. Si la función no devuelve nada, la matriz de salida se etiquetará como indefinida en esa posición. Para crear una matriz con una longitud diferente, puede usar Array.filter(). En ese caso, cuando el argumento funcional no devuelve nada, ese elemento se eliminará de la matriz de destino: const bands = [
{ name: «Led Zeppelin», year: 1968 },
{ name: «Pink Floyd», year: 1965 },
{ name: «Queen», year: 1970 },
{ name: «The Clash», year: 1976 },
{ name: «The Ramones», year: 1974 },
{ name: «R.E.M.», year: 1980 },
]; const setentaBandas = bandas.filtro(banda => { if (banda.año >= 1970 && banda.año < 1980) { return band; } }); // seventiesBands is an array holding only those bands satisfying the condition (band.year >= 1970 && banda.año < 1980) In this example, we take an array of objects holding rock bands and the year they were formed and then use bands.filter() to provide a function that will give us a new array holding only the bands from the 1970s. Array.reduce()Sometimes, you need to take a whole array and turn it into a single value. For that, you can use Array.reduce: // same band array as source const earliestBand = bands.reduce((earliestSoFar, band) => { volver banda.año < earliestSoFar.year ? band : earliestSoFar; }, { year: Infinity }); // Start with a band in the infinitely distant future console.log(earliestBand.name); // outputs “Pink Floyd” The function passed to reduce() has two arguments: the “accumulator” and the current element. The accumulator is what will be finally returned, and holds its state across each iteration, allowing you to “collect” everything into a single output.The reduce function is a very handy tool when you need it. As another quick example, say you wanted a string containing all the band names in a string. You could do this: const allBandNames = bands.reduce((accumulator, band) => { return acumulador + nombre.banda + «, «; }, «»); // El valor inicial es una cadena vacía Composición de funciones Las funciones integradas que has visto hasta ahora son fundamentales para la programación funcional (y su hermana mayor, la programación reactiva). Ahora, consideremos la idea de vincular funciones para lograr alguna funcionalidad deseada. Dos de las funciones de vinculación más básicas e importantes son componer() y cadena(). Muchas bibliotecas de utilidades y programación funcional los incluyen, pero también son fáciles de implementar. El siguiente ejemplo le ofrece una visión clara de cómo funcionan: const compose = (…fns) => (x) => fns.reduceRight((v, f) => f(v), x); cadena constante = (…fns) => (xs) => xs.reduce((acc, x) => acc.concat(fns.reduceRight((v, f) => f(v), x)) , []); compose() combina muchas funciones, de modo que la salida de cada función se introduce en la siguiente, de derecha a izquierda (según su orden pasado a la función). chain() hace lo mismo, pero de izquierda a derecha. Estas funciones también le dan un vistazo a reduceRight(), la imagen reflejada de reduce(), que ya ha visto. La función reduceRight() le permite acumular retrocediendo a través de los argumentos funcionales. Las funciones compose() y chain() no son específicas de las matrices, pero se pueden usar con ellas. Aquí hay un ejemplo simple del uso de compose() con una matriz: números constantes = [1, 4, 2, 8, 5, 7]; // Definir funciones reutilizables de orden superior: const findEvenNumbers = arr => arr.filter(n => n % 2 === 0); const números dobles = arr => arr.map(n => n * 2); const sortNumbers = arr => arr.sort((a, b) => a – b); // Componer funciones para crear transformaciones complejas: const ProcessNumbers = compose(sortNumbers, doubleNumbers, findEvenNumbers); const Números procesados ​​= Números de proceso(números); console.log(Números procesados); // Producción: [4, 8, 16]
ConclusiónOrganizar funciones es fundamental para la programación tanto funcional como reactiva. Le permite reutilizar y combinar funciones en nuevas funciones. En esencia, puede definir funciones compuestas que se componen de las capacidades de otras más enfocadas. Esto es similar, conceptualmente, a cómo un programador orientado a objetos piensa acerca de componer aplicaciones a partir de objetos. Debido a que las funciones expresan su trabajo de una manera tan minimalista (con la entrada y la salida como toda su superficie API), proporcionan un enfoque excepcionalmente limpio. Por supuesto, a medida que te vuelves más sofisticado, pierdes parte de esta claridad. Incluso las funciones compose() y chain() dejan atrás algo de la elegancia de las funciones simples. En general, manejar funciones de matriz como las que hemos visto aquí, usando funciones integradas de JavaScript como map() y filter(), es una excelente aplicación del poder de la programación funcional. Copyright © 2024 IDG Communications, Inc.

Source link