Understanding Array.map by Rolling Your Own
Tags: javascript, wevdev, beginners, tutorial
Published: 2020-04-23 12:30:57

---
title: Understanding Array.map by Rolling Your Own
published: true
description: One way to understand JavaScript methods is to roll your own version. Today, let's write Array.map!
tags: javascript, wevdev, beginner, tutorial
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/omeetw8gvune4omrxw8d.png
---

One way to understand JavaScript methods is to roll your own version. Today, let's write `Array.map`!

It turns out, `Array.map` takes two arguments:
- The function that will be applied as you loop over the array
- A `thisArg`, which will be a reference to an object that will be the `this` context in the provided function. 

In my experience, I have not really used the second argument, but we'll want to be sure to include it.

# Write the Interface

Since I'm not interested in extending the `Array` prototype, I'll just create a separate `map` function. Therefore, I'll actually pass the array as an argument, meaning we'll have three total arguments:

```javascript
function map(arr, fn, thisArg) {
  // Magic goes here
}
```

# Applying the Function to Each Element

The `fn` we provide must be applied to each element of the array. Let's make that happen.

```javascript
function map(arr, fn, thisArg) {
  const len = arr.length;
  const result = new Array(len);
  for (let i = 0; i < len; i++) {
    if (i in arr) {
      result[i] = fn(arr[i], i, arr);
    }
  }
  return result;
}
```

Importantly, we pass _three_ arguments to `fn`: The current array element, the index of the current array element, and the original input array. Let's see this in action:

```javascript
const mapped = map([1, 2, 3], el => el * 2);

console.log(mapped);
// [2, 4, 6]
```

Great, looks like the basics are working! This example doesn't include use of the `i` or `arr` passed to our `fn`, but you can test that on your own.

# Finally, the thisArg

Let's not forget the `thisArg`! If `thisArg` is provided, we want to make sure we `bind` our provided function to the `thisArg`. Here's the amended code to make it work:

```javascript
function map(arr, fn, thisArg) {
  fn = thisArg === undefined ? fn : fn.bind(thisArg);
  const len = arr.length;
  const result = new Array(len);
  for (let i = 0; i < len; i++) {
    if (i in arr) {
      result[i] = fn(arr[i], i, arr);
    }
  }
  return result;
}
```

And here it is in action. (Note that my provided function can't be an arrow function since you cannot rebind an arrow function's `this` reference.)

```javascript
const obj = {
  num: 10,
};

const mapped = map(
  [1, 2, 3],
  function (el) {
    return el + this.num;
  },
  obj
);

console.log(mapped);
// [11, 12, 13]
```

And we can see `this` refers to `obj`!

# Bonus: Write Your Own with Test-Driven Development

I wrote this `map` function using Test-Driven Development (TDD)! I laid out tests for all the scenarios that needed to pass for `Array.map` and then, one-by-one, reworked the code to make them pass. Consider using this method if you try this with other built-in JS methods.

Here are the tests I used for the `map` function:

```javascript
describe("array", () => {
  describe("map", () => {
    it("maps a simple array", () => {
      const arr = [1, 2, 3];
      const fn = el => el * 10;
      const answer = arr.map(fn);
      expect(map(arr, fn)).toEqual(answer);
    });
    it("maps an empty array", () => {
      const arr = [];
      const fn = el => el * 10;
      const answer = arr.map(fn);
      expect(map(arr, fn)).toEqual(answer);
    });
    it("maps an array with holes", () => {
      const arr = [1, 2, , , 3];
      const fn = el => el * 10;
      const answer = arr.map(fn);
      expect(map(arr, fn)).toEqual(answer);
    });
    it("uses thisArg", () => {
      const obj = {
        0: "foo",
        1: "bar",
        2: "baz"
      };
      const arr = [1, 2, 3];
      const fn = function(el, i) {
        return this[i] + el;
      };
      const answer = arr.map(fn, obj);
      expect(map(arr, fn, obj)).toEqual(answer);
    });
    it("uses the idx and arr parameters", () => {
      const arr = [1, 2, 3];
      const fn = (el, idx, arr) => JSON.stringify([el, idx, arr]);
      const answer = arr.map(fn);
      expect(map(arr, fn)).toEqual(answer);
    });
  });
```

I hope you enjoyed this! Let me know if you end up rolling your own versions of other built-in methods.