Navigate back to the homepage
BLOG
Browse Repo
Back

Forever Functional: The mighty reduce, part 2

Federico Kereki
November 17th, 2021 · 4 min read

In our previous article we discussed how `reduce()` is powerful enough to provide alternatives to other methods, such as `map()`, `filter()`, and `reduceRight()`. We didn’t cover, however, all the available higher-order functions that JavaScript provides. In this article we’ll show how to emulate more methods:

As in the other article, we’ll see that all these methods can be emulated by properly using `reduce()`. And, let’s also hurry up and answer a question: should we do such emulation? The answer, again, will be that this is more an exercise in “lateral thinking” about finding new ways of doing things, and about learning more about JavaScript and programming in general. We may not end up using these alternative implementations, but the techniques that we’ll use may help in other places.

Testing for conditions: every() and some()

The two `.every()` and `.some()` methods greatly simplify testing arrays for conditions.

• `every()` is true, if and only if every element of an array satisfies a given predicate; and
• `some()` is true if there is at least one element in an array, which satisfies the given predicate.

Oh, an aside just for Boolean logic buffs: we may use the duality principle, and say that

• `some()` is false, if and only if no element of an array satisfies a given predicate

How can we implement `.every()` and `.some()` based on `.reduce()`? The following ways are a possibility.

`.css-194xkmm{position:absolute;right:22px;top:24px;padding:8px 12px 7px;border-radius:5px;color:#6f7177;-webkit-transition:background 0.3s ease;transition:background 0.3s ease;}.css-194xkmm:hover{background:rgba(255,255,255,0.07);}.css-194xkmm[data-a11y="true"]:focus::after{content:"";position:absolute;left:-2%;top:-2%;width:104%;height:104%;border:2px solid var(--theme-ui-colors-accent,#394EFF);border-radius:5px;background:rgba(255,255,255,0.01);}@media (max-width:45.9375em){.css-194xkmm{display:none;}}1const every = (arr, fn) => 2  arr.reduce((x,y) => x && fn(y), true);34const some = (arr, fn) => 5  arr.reduce((x,y) => x || fn(y), false);`

The first implementation ANDs together all the elements in the array, starting with a `true`; the second ORs all elements, and starts with `false`. Because of how JavaScript short-circuits the evaluation of boolean expressions, the `fn()` predicate won’t be evaluated again, once the value of `.every()` or `.some()` has been calculated. (For instance, if a value satisfies the predicate, the accumulator in `.some()` will become true, and future evaluations of `x || fn(y)` won’t do the second part.) Let’s test this, with the help of our `addLogging()` higher order function from our previous article on higher order functions.

`1const arr = [22, 9, 60, 56, 12, 4];23const isOdd = (x) => x % 2 === 1;45every(arr, addLogging(isOdd));6// Enter isOdd 227// Exit  isOdd false89some(arr, addLogging(isOdd));10// Enter isOdd 2211// Exit  isOdd false12// Enter isOdd 913// Exit  isOdd true`

The `isOdd()` function tests if its parameter is odd. If we try to see if all the elements in the `arr` array are odd, the first test already fails, and we see that `isOdd()` wasn’t called again. If testing whether some elements of the array are odd, after testing 22 and 9 (when the test succeeded) no more calls to the predicate are done. If you wish to, however, think about how to also skip processing the rest of the array. Even if the predicate is no longer called, `.reduce()` will still go through the whole array; a nice exercise!

Searching an array: find() and findIndex()

Let’s now turn to studying `find()` and `findIndex()`. The first one searches an array for the first element that satisfies a given predicate and returns it, or `undefined` otherwise. The second one is similar but returns the index of the element, or -1 if none was found.

The implementation for the former is similar to that of `every()`. We’ll go through the array searching for a value that satisfies the condition, starting with an `undefined` accumulator. When we find an appropriate element, we change the accumulator to the element. For the latter we’ll do a similar thing, but we’ll keep track of the position of the array, starting with -1.

`1const find = (arr, fn) => 2  arr.reduce((x,y) => (x===undefined && fn(y) ? y : x), undefined);34const findIndex = (arr, fn) => 5  arr.reduce((x,y,i) => (x===-1 && fn(y) ? i : x), -1);`

You must remember that `.reduce()` calls your reducing function providing the value of the accumulator, the current element of the array, and finally its index; the latter is the one that we’ll keep in `findIndex()`.

Open Source Session Replay

Debugging a web application in production may be challenging and time-consuming. OpenReplay is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder. OpenReplay is the only open-source alternative currently available.

Flattening an array: flat() and flatMap()

In ES2019 the new `flat()` and `flatMap()` methods were added to the language.

The flat operation creates a new array, recursively concatenating all its elements (that may be arrays) up to a specified depth; by default, just 1. For example, imagine you query an API and it returns an array with the orders of a customer, and for each order you have an array of products: something like `arr=[[22, 9, 60], [56], [12, 4]]`. (His first order had three products; the next, one; and the last, two.) We can flatten this into a single array by using `flat()`: `arr.flat()` would be `[22, 9, 60, 56, 12, 4]`.

Flattening the array just one level (as we did in the previous paragraph) is simple. We could do it by spreading, but since we’re focusing on the `reduce()` method, let’s go that way.

`1const flatOne = arr => 2  arr.reduce((f, v) => f.concat(v), []);`

We start with an empty array, and we concatenate each element of the array to it. If an element happens to be an array itself, we’ll get it “flattened”.

Let’s now see how we can totally flatten an array, meaning that if some elements are arrays, whose elements are also arrays, and so on, all will get flattened. Recursion suits the problem naturally; each time we process an element of the array, if it’s an array, we flatten it.

`1const flatAll = arr =>2  arr.reduce((f, v) => f.concat(Array.isArray(v) ? flatAll(v) : v), []);`

Given these two functions, we can now flatten an array at any desired number of levels. We’ll use recursion again, and we get a nice implementation.

`1const flat = (arr, n = 1) =>2  n === Infinity                       3    ? flatAll(arr)                     // [1]4    : n === 15      ? flatOne(arr)                   // [2]6      : flat(flatOne(arr), n - 1);     // [3]`

According to the `flatMap()` specification, if the level is Infinity, the whole array must be totally flattened, so that’s what we do [1]. If we want to flatten it just one level, we use our previous function [2]. And, finally, if we want to flatten the array several levels, we flatten it one level, and we recursively flatten it more levels [3].

What about `flatMap()`? OK, we’ll cheat here: that operation is just a `map()` followed by a `flat()` — and as we’ve already seen how these two operations can be emulated with the help of flat(), we don’t have to do anything special here!

Summary

We have now completed studying how `reduce()` may be certainly considered the most flexible and potent functional method for JavaScript programmers. Even if we don’t use the implementations seen here, we’ve studied several techniques and algorithms that may form the base for our own coding. We’ve always been trying to work functionally, and we now know what’s the mightiest tool for that: `reduce()`!

Build a Movie Search App with Petite-Vue: A lightweight alternative to VueJS

Petite-Vue is a lightweight alternative to VueJS, learn how to use it with this practical tutorial

November 10th, 2021 · 3 min read

Building an Encyclopedia with React and Wikipedia Search API

Learn how to query a REST API using Fetch with this practical tutorial

November 8th, 2021 · 3 min read