Exercise - Array.prototype methods with reduce
Tags: javascript, array, exercise
Published: 2019-11-05 10:02:37

For those who read my previous articles, you might know my fascination for the Array.prototype method `reduce` (Click [here](https://dev.to/marwaneb/toolkit-map-filter-reduce-in-arrays-4mp7#reduce-sometimes-simple-is-not-enough) to find out more about this function).

Talking with a colleague of mine, we actually realised that it was so flexible we could probably implement all the other Array.prototype methods using it instead. Let's try that together in this article!

Quick overview of what we will do:

1. Only write pure functions. It is a _"must do"_ if you interested in declarative and functional programming and I find `reduce()` really helpful when you go on that route.
2. Re-implement all the accessor and iteration methods. Not that I'm not interested in mutations but I don't use them on regular basis (see 1.).
3. Only use one `reduce`.

But first, I would really advise you to __DO IT YOURSELF__. You don't really have to write them all down but I found this exercise really interesting for few reasons.

- It is a great way to learn `reduce()` if you're not too familiar with it.
- I found myself rediscovering some `Array.prototype` methods like `some` that I haven't used for ages and could be really interesting to use in my day to day code.

## Accessor methods

### Array.prototype.length

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length)

> The `length` property of an object which is an instance of type Array sets or returns the number of elements in that array. The value is an unsigned, 32-bit integer that is always numerically greater than the highest index in the array.

Yes, I know `Array.prototype.length` is a property. I still find this property quite magic since - unlike most other properties you would find - it automatically mutates based on the length of your array and therefore could belong to this analysis.

```js
const length = arr => () => arr.reduce((acc) => acc + 1, 0);
```

### Array.prototype.concat()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)

> The `concat()` method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

```js
const concat = arr1 => arr2 => arr2.reduce((acc, el) => [...acc, el], arr1);
```

### Array.prototype.includes()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)

> The `includes()` method determines whether an array includes a certain value among its entries, returning `true` or `false` as appropriate.

```js
const includes = arr => element => arr.reduce((acc, el) => acc || (el === element), false);
```

### Array.prototype.indexOf()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)

> The `indexOf()` method returns the first index at which a given element can be found in the array, or -1 if it is not present.

```js
const indexOf = arr => element => arr.reduce((acc, el, index) => (
  (acc === -1 && el === element) ? index : -1),
  -1,
);
```

### Array.prototype.join()

[Link]((https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join))

> The `join()` method creates and returns a new string by concatenating all of the elements in an array (or an array-like object), separated by commas or a specified separator string. If the array has only one item, then that item will be returned without using the separator.

```js
const join = arr => (separator = '') => arr.reduce((acc, el) => `${acc}${separator}${el}`);
```

### Array.prototype.lastIndexOf()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf)

> The `lastIndexOf()` method returns the last index at which a given element can be found in the array, or -1 if it is not present. The array is searched backwards, starting at fromIndex.

```js
const lastIndexOf = arr => element => arr.reduce((acc, el, index) => (el === element ? index : -1), -1);
```

### Array.prototype.slice()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)

> The `slice()` method returns a shallow copy of a portion of an array into a new array object selected from `begin` to `end` (end not included) where begin and end represent the index of items in that array. The original array will not be modified.

```js
const isBetween = i => (begin, end) => i >= begin && i < end;
const slice = arr => (begin, end) => arr.reduce((acc, el, index) => isBetween(index)(begin, end) ? [...acc, el] : acc, []);
```

### Array.prototype.toString()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString)

> The toString() method returns a string representing the specified array and its elements.

```js
const toString = arr => () => arr.reduce((acc, el) => `${acc},${el}`);
```

### Array.prototype.toLocaleString()

[Link]((https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toLocaleString))

> The toLocaleString() method returns a string representing the elements of the array. The elements are converted to Strings using their toLocaleString methods and these Strings are separated by a locale-specific String (such as a comma “,”).

```js
const toLocaleString = arr => () => arr.reduce((acc, el, index) => `${acc}${index === 0 ? '' : ','}${el.toLocaleString()}`, '');
```

## Iteration methods

### Array.prototype.entries()

[Link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries)

> The `entries()` method returns a new Array Iterator object that contains the key/value pairs for each index in the array.

```js
const entries = arr => () => arr.reduce((acc, el, index) => [...acc, [index, el]], []);
```

### Array.prototype.every()

[Link]((https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every))

> The `every()` method tests whether all elements in the array pass the test implemented by the provided function. It returns a Boolean value.
>
> Note: This method returns true for any condition put on an empty array.

```js
const every = arr => predicate => arr.reduce((acc, ...rest) => acc && predicate(...rest), true);
```

### Array.prototype.filter()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter): The `filter()` method creates a new array with all elements that pass the test implemented by the provided function.

```js
const every = arr => predicate => arr.reduce((acc, el, ...rest) => (predicate(el, ...rest) ? [...acc, el] : acc), []);
```

### Array.prototype.find()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find): The find() method returns the value of the first element in the provided array that satisfies the provided testing function.

This one is quite tricky: my first reaction was to write down this code.

```js
const find = arr => predicate => arr.reduce(
  (acc, el, index, array) => (acc === undefined && predicate(el, index, array)) ? el : undefined,
  undefined,
);
```

Then I realised an edge case that would actually make this function behave differently from the original `find` method. Here is the edge case:

```js
console.log([undefined, null].find(el => !el)); // Returns: undefined
console.log(find([undefined, null])(el => !el)) // Returns: null
```

How to solve this edge case? Well looks like our only source of truth will be the index of the found element. Let's try that one instead:

```js
const find = arr => predicate => arr.reduce(
  (acc, el, index, array) => (acc[1] === -1 && predicate(el, index, array)) ? [el, index] : acc,
  [undefined, -1],
)[0];
```

This way we are sure that the first element that is found will be returned by our function.

### Array.prototype.findIndex()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex): The `findIndex()` method returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1, indicating that no element passed the test.

This one should be quite straight forward considering the code written above

```js
const findIndex = arr => predicate => arr.reduce(
  (acc, el, index, array) => (acc[1] === -1 && predicate(el, index, array)) ? [el, index] : acc,
  [undefined, -1],
)[1];
```

### Array.prototype.forEach()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach): The `forEach()` method executes a provided function once for each array element.

```js
const forEach = arr => callback => arr.reduce(
  (_, ...rest) => { callback(...rest); },
  [],
);
```

### Array.prototype.keys()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/keys): The `keys()` method returns a new `Array Iterator` object that contains the keys for each index in the array.

```js
const keys = arr => () => arr.reduce((acc, _, index) => [...acc, index], []);
```

### Array.prototype.map()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map): The `map()` method creates a new array with the results of calling a provided function on every element in the calling array.

```js
const map = arr => callback => arr.reduce((acc, ...rest) => [...acc, callback(...rest)], []);
```

### Array.prototype.some()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some): The some() method tests whether at least one element in the array passes the test implemented by the provided function. It returns a Boolean value.
>
> Note: This method returns false for any condition put on an empty array.

```js
const some = arr => predicate => arr.reduce((acc, ...rest) => acc || predicate(el, ...rest), false);
```

### Array.prototype.values()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values): The values() method returns a new Array Iterator object that contains the values for each index in the array.

```js
const values = arr => () => arr.reduce((acc, el) => [...acc, el], []);
```

### Array.prototype.reduceRight()

> [Definition](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight): The reduceRight() method applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value.

For this one, my first idea was obviously to reverse the array and then apply a `reduce()`. Lol funny but not optimal as we are iterating twice on the array 🙃.

```js
const reduceRight = arr => (...params) => arr.reduce((acc, el) => [el, ...acc]).reduce(...params);
```

After searching for ages, I must say I'm quite disappointed on this one... I have not been able to find a short and clean way to implement `reduceRight()` with only one `reduce()`... It is extremely frustrating and I would love to hear someone who has a suggestion regarding it!

## Conclusion

Among a lot of trivial methods to implement, several one have been interesting to implement:

- `find()`: this edge-case actually made the implementation a bit stiffer than I thought. It really pushed me to have a critical eye on when it comes to the predicates I'm using.
- `reduceRight()`: this is actually my biggest frustration. I would be really interested to know if one of you can solve it and how to!