Higher Order Array Methods in JavaScript
Tags: arrays, javascript, beginners, webdev
Published: 2021-01-31 17:50:45

## Introduction
Higher order functions are functions that operate on other functions, either by receiving them as arguments or by returning them. It is a function that accepts a function as a parameter or returns a function as the output.

In this article, I am going to show you the most commonly used such methods when it comes to arrays.

## `Array.prototype.map`
This is one of the simplest functions you are going to use while working with Arrays. It forms a new array by calling the function passed into it as an argument, **on each and every element of the Array. It will `map` each of the return values of the callback and create a new array**.

The callback passed to the `map()` function can accept any of the three arguments: `item`, `index`, `array`.

#### Example 1
Given an array of integers, create a new array in which the double of each integer in the first array is stored, and log it to the console.

**Solution**:
```js
const givenArray = [477, 8914, 40461, 599148];
const newArray = givenArray.map(n => n * 2);
console.log(newArray); // console: [954, 17828, 80922, 1198296]
```

#### Example 2
Given an array of singular nouns, create a new array that stores the plural noun of each of the words in the first array, and log it to the console (assume that the singular nouns can be made plural by adding a 's').

**Solution**:
```js
const givenArray = [ 'pen', 'book', 'code' ];
const newArray = givenArray.map(w => w + 's');
console.log(newArray); // console: ['pens', 'books', 'codes']
```

## `Array.prototype.filter`
The `filter()` function is what you'd use if you're creating a search bar from a given list of items, for example. The `filter()` method also creates a new array by executing the passed callback **on every element of the array**, and keeps it in the resulting array **IF and ONLY IF the element passes the Boolean test returned by the callback**.

The callback passed into the `filter()` method accepts any of the three arguments: `item`, `index` and `array`; same as the `map()` method.

#### Example 1
Given an array of costs of different products, create a new array with the costs from the input array if the cost is <= $350, and print it to the console.

**Solution**:
```js
const givenArray = [390, 190, 311.85, 67, 19048, 5000, 670];
const newArray = givenArray.filter(c => c <= 350);
console.log(newArray) // console: [190, 311.85, 67];
```

#### Example 2
Given an array of objects with the city name and population, create an array with objects from the first array if the population of that particular city is >= 5 million.

**Solution**:
```js
const givenArray = [
  { "name": "Shanghai", "population": 24300000 },
  { "name": "Los Angeles", "population": 3792621 },
  { "name": "New Delhi", "population": 21800000 },
  { "name": "Mumbai", "population": 18400000 },
  { "name": "Chicago", "population": 2695598 },
  { "name": "Houston", "population": 2100263 },
];
const newArray = givenArray.filter( ({ population }) => population >= 5000000);
console.log(newArray); // console: [{name: "Shanghai", population: 24300000}, {name: "New Delhi", population: 21800000}, {name: "Mumbai", population: 18400000}]
```

## `Array.prototype.reduce`
The `reduce()` method creates a new array, executing the callback passed into it on every element, and outputs a single value. It does something on every element and keeps a record of the calculations in an accumulator variable and when no more elements are left, it returns the accumulator.

The `reduce()` function itself takes two inputs: (a) the reducer function or callback; (b) an optional starting point or `initialValue`.

The reducer function or the callback accepts 4 arguments: `accumulator`, `currentItem`, `index`, `array`.

If the optional `initialValue` is given, the `accumulator` at the first iteration will be equal to the `initialValue` and the `currentItem` will be equal to the first element in the array. Otherwise, the `accumulator` would be equal to the first item in the input array, and the `currentItem` will be equal to the second item in the array.

Sounds confusing? Let's have a look at two examples:

#### Example 1
(i) Given an array of numbers, find the sum of every element in the array, and log it to the console.

**Solution**:
```js
const givenArray = [1, 2, 3, 4, 5];
const sum = givenArray.reduce((acc, curr) => acc + curr);
console.log(sum); // console: 15
```
Let's have a look at the accumulator and the current value...:
- at the first iteration: `acc=1``(givenArray[0])`, `curr=2``(givenArray[1])`
- at the second iteration: `acc=3``(givenArray[0] + givenArray[1])`, `curr=3``(givenArray[2])`
- at the third iteration: `acc=6``(givenArray[0] + givenArray[1] + givenArray[2])`, `curr=4``(givenArray[3])`
- at the fourth iteration: `acc=10``(givenArray[0] + givenArray[1] + givenArray[2] + givenArray[3])`, `curr=5``(givenArray=[4])`
- finally: `acc=15` (sum of all elements) (array iteration ended)

You can view this yourself by running a `console.log` inside the function like so: `console.log("iteration: acc="+acc+" curr="+curr);`

(ii) Given an array of numbers, find the sum of every element in the array, starting with 8, and log the result to the console

**Solution**:
```js
const givenArray = [1, 2, 3, 4, 5];
const sum = givenArray.reduce((acc, curr) => acc + curr, 8);
console.log(sum); // console: 23
```
Note: Here, we are passing the optional `initialValue` parameter to the `reduce()` function, saying that we want to start with 8 and do whatever we want inside the callback.
Again, you can test the values of `acc` and `curr` and by adding a `console.log` like above.

#### Example 2
Given an array of numbers, find the average of them, and log it to the console.

**Solution**:
```js
const givenArray = [1, 2, 3, 456, 108115, 45909.15154, 1988.1545, 145e8];
const average = (givenArray.reduce((acc, curr) => acc + curr)) / givenArray.length;
console.log(average); // console: 1812519559.288255
```

If you're confused from the third line, it basically calculates the sum first, and divides the return value by the length of `givenArray`. You could also use:

```js
const givenArray = [1, 2, 3, 456, 108115, 45909.15154, 1988.1545, 145e8];
const average = givenArray.reduce((acc, curr, index) => ( index == (givenArray.length -1) ) ? (acc + curr) / givenArray.length : acc + curr);
console.log(average); // console: 1812519559.288255
```

This is a complete no-no for readability and for the KISS principle, but I ain't a cop, use whichever method you like :wink:

## `Array.prototype.forEach`
The `forEach` method is similar to the `for(let i = 0; i < array.length, i++){}` syntax. It **loops through the array and runs the given callback for each of the elements of the array.**

The callback function passed to the `forEach` function can accept the `currentItem`, `index`, `array`.

#### Example
Given an array of numbers, log every number to the console(_wat?!_).

**Solution**:
```js
const arr = [1, 2, 3, 4, 5, 6, 7, 8];
arr.forEach(val => console.log(val));
/* console:
1
2
3
4
5
6
7
8
*/
```

The big difference between the `map` and `forEach` method is that the `map` method creates a new array, "mapping" the return value of the callback and create a new array, while the `forEach` method just iterates over the array.

## `Array.prototype.some` and `Array.prototype.every`
The `some` method **tests whether at least one element of the array complies with the given test in the callback, and returns `true` or `false`.**

The callback function passed to the `some` function can accept the `currentItem`, `index`, `array`.

#### Example 1
Given two arrays of numbers, test whether each of the arrays have at least one number that is > 5, and log the result to the console.

**Solution**:
```js
const givenArray1 = [1, 2, 3, 5, 8];
const givenArray2 = [1, 2, 3, 4, 5];
const testArray1 = givenArray1.some(n => n > 5);
const testArray2 = givenArray2.some(n => n > 5);
console.log(`givenArray1: ${testArray1}; givenArray2: ${testArray2}`); // console: givenArray1: true; givenArray2: false
```

The `every` method is pretty similar to the `some` method, but it **tests whether all the elements of the array complies with the given test in the callback, and returns `true` or `false`.**

The callback function passed to the `every` function can accept the `currentItem`, `index`, `array`.

#### Example 2
Given two arrays of numbers, test whether each of the arrays have all the numbers >= 5, and log the result to the console.

**Solution**:
```js
const givenArray1 = [10, 9, 8, 7, 6];
const givenArray2 = [5, 1, 2, 785, 45];
const testArray1 = givenArray1.every(n => n > 5);
const testArray2 = givenArray2.every(n => n > 5);
console.log(`givenArray1: ${testArray1}; givenArray2: ${testArray2}`); // console: givenArray1: true; givenArray2: false
```

## `Array.prototype.flat` and `Array.prototype.flatMap`
The `flat` method creates a **new Array with all the elements, and if the element is an array, then it "flattens" the element and adds all the sub-array elements to the returned array.** By default, it will only flatten the array upto 1 level.

The `flat` method can accept only one optional argument, the level or `depth` up till which the array will be "flattened".

#### Example 1
Given an array of arrays of numbers, find the sum of every number inside the array as well as the sub-arrays, and log the sum to the console.

**Solution**:
```js
const givenArray = [
  [1, 2, 3, 4, 5, 6],
  [10, 20, 30, 40, 50, 60],
  [100, 200, 300, 400, 500, 600]
];
const sum = 
  givenArray
    .flat() // flattens the array
    .reduce((acc, curr) => acc + curr); // finds the sum
console.log(sum); // console: 2331
```

The `flatMap` method is the combination of the `flat` method and the `map` method. **It first "flattens" the array, runs the callback for each element and "maps" the return value to the corresponding element, and finally returns the "flattened and mapped" array.** If you use something like: `arr.flatMap(...)`, its equivalent to `arr.flat().map(...)`. But, there's one catch: you cannot flatten the array by levels more than one, for that you need to use the `.flat(...).map(...)` syntax.

The `flatMap` method accepts the same arguments as the `map` method, and so does the callback.

#### Example 2
Given an array of arrays of users, create a new array with a single list of all the first names of the users, and log it to the console.

**Solution**:
```js
const users = [
  [
    { "firstName": "Lorem", "lastName": "Ipsum" },
    { "firstName": "Dolor", "lastName": "Sit" },
    { "firstName": "Amet", "lastName": "Consectetur" }
  ],
  [
    { "firstName": "Adipiscing", "lastName": "Elit" },
    { "firstName": "Etiam", "lastName": "Lobortis" },
    { "firstName": "Lorem", "lastName": "Elit" }
  ],
  [
    { "firstName": "Lorem", "lastName": "Ipsum" },
    { "firstName": "Dolor", "lastName": "Sit" },
    { "firstName": "Amet", "lastName": "Consectetur" }
  ],
  [
    { "firstName": "Adipiscing", "lastName": "Elit" },
    { "firstName": "Etiam", "lastName": "Lobortis" },
    { "firstName": "Lorem", "lastName": "Elit" }
  ]
];

const usersFirstNames = users.flatMap(usersGroup => usersGroup.map(u => u.firstName));

console.log(usersFirstNames); // console: ["Lorem", "Dolor", "Amet", "Adipiscing", "Etiam", "Lorem", "Lorem", "Dolor", "Amet", "Adipiscing", "Etiam", "Lorem"]
```

## `Array.prototype.find`
The `find` method **returns the first element of the array that satisfies the Boolean test in the callback**. If no element passes the Boolean test, `undefined` is returned.

The callback passed to the `find` function can accept any of the three arguments: `item`, `index`, `array`.

#### Example
Given an array of objects with fruits, find the 'apples' and log the corresponding object to the console.

**Solution**:
```js
const fruits = [
  {"name": "bananas", "quantity": 8},
  {"name": "cherries", "quantity": 3},
  {"name": "apples", "quantity": 80}
];

const apples = fruits.find( ({name}) => name == "apples" );
console.log(apples); // console: {"name": "apples", "quantity": 80}
```

## `Array.prototype.sort`
The `sort` method is self-explanatory: **it "sorts" an array in place and returns a sorted the array**. The default sort order is ascending. 

Note the words "in place". It means that the original array is changed and the same reference to the array is returned. So, `originalArray===newArray`, if nothing is sorted.

It takes a Function that specifies the criteria of sorting.

#### Example 1
Given an array of numbers, sort the array by ascending order and log the sorted array to the console.

**Solution**:
```js
const givenArray = [4, 5, 2, 1, 3];
givenArray.sort((a, b) => a - b);
console.log(givenArray);
```

#### Example 2
Given an array of contacts, sort them in alphabetical order by name, and log the sorted array.

**Solution**:
```js
const givenArray = [
  {"name": "Yosha Gamuda", "phone": 1234567890},
  {"name": "Portia Umeng", "phone": 4894759371},
  {"name": "Yosha Gamuda", "phone": 1234567890},
  {"name": "Portia Umeng", "phone": 4894759371}
];
givenArray.sort(({name1}, {name2}) => {
  name1 = name1.toUpperCase();
  name2 = name2.toUpperCase();
  return (name1 < name2) ? -1 : (name1 > name2) ? 1 : 0);
});
console.log(givenArray);
```
The way `sort()` works is a bit different than the others. Quoting [this MDN doc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort):
> If compareFunction is supplied, all non-undefined array elements are sorted according to the return value of the compare function (all undefined elements are sorted to the end of the array, with no call to compareFunction). If a and b are two elements being compared, then:

> - If compareFunction(a, b) returns less than 0, sort a to an index lower than b (i.e. a comes first).
> - If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different elements. Note: the ECMAScript standard only started guaranteeing this behavior in 2019, thus, older browsers may not respect this.
> - If compareFunction(a, b) returns greater than 0, sort b to an index lower than a (i.e. b comes first).
> - compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If inconsistent results are returned, then the sort order is undefined.
>
> To compare numbers instead of strings, the compare function can subtract b from a. The following function will sort the array in ascending order (if it doesn't contain Infinity and NaN).

## Conclusion
I know this article has ABSOLUTELY LEVIATHAN AMOUNT of information. There are loads of others, but essentially, you need not know every method in `Array.prototype` to work with arrays. Thank you for reading this article, I hope you enjoyed it. Any feedback will be much, much appreciated: good or bad ;)