Array map
Tags: javascript, functional
Published: 2020-01-27 18:05:31

When we work with data, we generally need to do something with it. Whether editing the data, sorting the data, transforming the data or some other task, we must be able to understand what the data is and what we want to do with it. One of the things we can do is altering the data and this is where a map function comes in. A map function takes in a collection and alters each item of the collection based on a provided mapper function.

In vanilla JavaScript, we could do the following to demonstrate this technique:

```javascript
const purchases = [1.30, 20.40, 14.76];

function withTaxMapperFn(item) {
  const withTax = item * 1.2;
  return Math.round(withTax * 100) / 100;
}

const withTax = purchases.map(withTaxMapperFn);
console.log(withTax); // [1.56, 24.48, 17.71]
```

Our aim is to understand how the native `map` function works and then to build our own implementation for it. 

## Tests

```javascript
describe('map', () => {
  it('should apply the callback correctly', () => {
    const collection = [1, 2, 3];
    const mapperFn = item => item * 2;
    const actual = map(collection, mapperFn);
    const result = [2, 4, 6];
    expect(actual).toStrictEqual(result);
  });
});
```

Realistically we just need to test that a given `mapperFn` returns the correct results when passed alongside the collection and so I kept the test relatively simple. We have a collection and a mapper function that doubles each item in the collections, eventually returning a new collection that contains the doubled values as expected.

## Implementation

The native `map` function in javascript has the following signature:

```javascript
let new_array = arr.map(function callback( currentValue[, index[, array]]) {
    // return element for new_array
}[, thisArg])
```
> Source: [MDN Docs - Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Syntax)

In short we can provide a function which takes the current value, the index of that value in the collection and a reference to the collection itself. We will replicate this structure for our custom map function when we implement it. With that said, here is the implementation I have went with:

```javascript
/**
 * @function map
 * @description A function to transform values of a collection
 * @param {Array} collection - The collection to adapt
 * @param {Function} mapperFn - The action to commit upon each item
 * @returns {Array} A new array with the mapped results
 */
function map(collection, mapperFn) {
  const output = [];
  const clone = [...collection];
  for (let index = 0; index < clone.length; index++) {
    const altered = mapperFn(clone[index], index, clone);
    output.push(altered);
  }
  return output;
}
```

Here we create the function `map`. This function creates 2 internal arrays, one for the output and another as a `clone` of the `collection`. I create a clone so that if someone passes a mapper function that alters the array reference, the original `collection` won't be altered, only the `clone`. We then loop each item in the cloned array and call our `mapperFn` providing the current item, the index of that item and a reference to the `clone` array so as to match the JavaScript native `map` signature outlined earlier in this article. Lastly we push the return value of the `mapperFn` into the `output` array and once the loop finishes, we return the `output` array with the altered values.

Using our example of the native use case at the top of this article, we can implement the same code but use our custom `map` function:

```javascript
const purchases = [1.30, 20.40, 14.76];

function map(collection, mapperFn) {
  const output = [];
  const clone = [...collection];
  for (let index = 0; index < clone.length; index++) {
    const altered = mapperFn(clone[index], index, clone);
    output.push(altered);
  }
  return output;
}

function withTaxMapperFn(item) {
  const withTax = item * 1.2;
  return Math.round(withTax * 100) / 100;
}

const withTax = map(purchases, withTaxMapperFn);
console.log(withTax); // [1.56, 24.48, 17.71]
```

## Conclusions

Understanding how the tools that we use actually work is an important part of any craft and that goes no-less so for software engineering. Moving forward we will look at a couple of other commonly used array functions such as `filter`, `reduce` and `sort` to gain a deeper understanding of what happens under the hood. Map is a function provided for arrays in most common languages, thus we began here to understand such implementations. We have much more to explore and hopefully this inspires you to try re-inventing the wheel for other things in your language of choice, to understand how it works!