An Interest In:
Web News this Week
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
- April 18, 2024
- April 17, 2024
Reduces many uses
The reduce
array method is often introduced along with map
and filter
, but it is such a powerful method that I felt it deserved a post of its own. The traditional example used to introduce reduce
is the following function that will calculate the sum of all the elements in an array:
const array = [1, 2, 3, 4, 5];const sum = array.reduce((a, b) => a + b);
From this example, you might start developing an intuition that this method reduces the elements in the array down to a single value, and it certainly can and does in many cases. However, since a value can be pretty much anything in JavaScript, the reduced result may not necessarily be a single primitive value or even smaller that the original array (if you can come up with some notion of size to compare them).
Here is the abstraction that reduce provides:
const array = [1, 2, 3, 4, 5];const INITIAL_VALUE = 0;const reduceFunction = (accumulator, element) => accumulator + element;// Without reducelet accumulator = INITIAL_VALUE;for (let i = 0; i < array.length; i++) { accumulator = reduceFunction(accumulator, array[i])}// With reduceconst accumulator = arrray.reduce(reduceFunction, INITIAL_VALUE);
The reduceFunction
, also known as the reducer, takes two values and returns a value of the same type as the first argument. This returned value is supplied as the first argument of the next iteration. If no initial value is given, the first element in the array will be used as the initial value. The implementation of the reduce
method on the array prototype makes it an instance of a Foldable, and Haskell calls this function foldl
(for fold from the left). Let's take a look at some things reduce
can do!
Map
You can use reduce
to replace map
. The benefits of this approach are not immediately obvious, but it will be helpful when we cover transducers in the future.
const array = [1, 2, 3, 4, 5];const mapFunc = (number) => number * 2;// With mapconst newarray = array.map(mapFunc);// With reduceconst mapReducer = (func) => (accumulator, element) => [...accumulator, func(element)];const newarray = array.reduce(mapReducer(mapFunc), []);
Filter
You can use reduce
to replace filter
as well, and this will also be helpful when we talk about transducers.
const array = [1, 2, 3, 4, 5];const predicate = (number) => number % 2 === 0;// With filterconst newarray = array.filter(predicate);// With reduceconst filterReducer = (predicate) => (accumulator, element) => predicate(element) ? [...accumulator, element] : accumulator;const newarray = array.reduce(filterReducer(predicate), []);
Various aggregates
Pretty much anything that you could think of creating from an array can be created using reduce
. I particularly like this implementation of creating the upper triangular matrix of an array. The reduce function takes an optional third argument which is the index of the element. (It also takes a fourth optional argument which is the array itself).
// Using nested for loopsconst upperTriangle = (arr) => { let triangle = []; for (let first = 0; first < arr.length; first++) { for (let second = first + 1; second < arr.length; second++) { triangle.push([arr[first], arr[second]]); } } return triangle;};// Using reduce and mapconst upperTriangle = (arr) => arr.reduce((triangle, first, i) => { const rest = arr.slice(i + 1); const pairs = rest.map(second => [first, second]); return [triangle, pairs].flat(); }, []);
Function composition
You read that right. You can implement function composition with reduce
!
const toWords = (string) => string.split(" ");const count = (array) => array.length;const wpm = (wordCount) => wordCount * 80;const speed = (string) => [toWords, count, wpm] .reduce((composed, fn) => fn(composed), string);
Recursive functions
If you can convert a recursive function to an iterative approach, you can also implement it using reduce
. Recursive functions are often used because of their semantic definitions, but using reduce
does not have the issue of potentially filling up the function call stack while enabling declarative definitions if done well.
const factorial = (number) => number === 0 ? 1 : number * factorial(number - 1);const factorial = (number) => Array(number) .fill(number) .reduce((acc, elem, i) => acc * (elem - i));
Sum and friends
Let's revisit the sum function that we started with. It turns out that there are a bunch of examples that follow a similar pattern:
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce((a, b) => a + b, 0);const product = numbers.reduce((a, b) => a * b, 1);const min = numbers.reduce((a, b) => (a < b ? a : b), Infinity);const max = numbers.reduce((a, b) => (a > b ? a : b), -Infinity);const booleans = [true, false, false, true];const any = booleans.reduce((a, b) => a || b, false);const all = booleans.reduce((a, b) => a && b, true);
In all of these cases, the initial value can be left out, but I included them for clarity. All of these reducers take two elements of the same type and return another thing of the same type. This property combined with appropriate starting values (known as identities) forms the definition of a Monoid. In the next post, we will take a closer look at Monoids and the various places they come up in programming.
Hopefully this post has given you a better intuition for the uses of reduce. Combined with map
and filter
, I seldom find myself writing a for or while loop anymore. The imperative loops are more helpful if you have to do something a certain number of times, but as we'll see soon, it is better to work with expressions of values than simple statements.
Original Link: https://dev.to/sethcalebweeks/reduces-many-uses-57dl
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To