# Are you using map, forEach, and reduce wrong?

In JavaScript, `Array.prototype.map`, `Array.prototype.forEach`, and `Array.prototype.reduce` are used heavily in functional-style programming. However, I meet many developers missing a clear mental model of when or how to use each function.

This is a problem because we use these functions not just for their behaviour but to communicate our code's intent. These functions reveal crucial details about the author's mental model of the code, the problem, and the solution.

In this article, I will attempt to couple a technical understanding of these functions to a semantic understanding in your mind. As a result, you should have a mental model of when to use these functions and why, plus what it means when you read code using them.

## Array.prototype.map

Let's start with a simplified version of the official TypeScript definition for map and break it down. I have trimmed the type parameters down to the simplest and most common use case:

```ts
map<U>(
  callbackfn: (value: T) => U
) => U[];
```

Above, we have a `map` function that accepts a type argument `U`, a callback function, and returns an array (`U[]`). 

The callback function takes a value of type `T` and returns a `U`. So it accepts a function that will take a value and transform it into another value.

Map calls this transformation function, the callback, for every element (`T`) in the array. The callback function transforms `T` into `U`, which `map` uses to transform each element, returning a new array of `U[]`. 

Now I have lied to you slightly in the above explanation. Transforming implies a mutative operation. However, `map` does not and should not mutate anything. Instead, it returns an entirely new array (`U[]`) where every element has a 1 to 1 relationship with each element of `T[]`. We call the function `map` because it _maps_ one type onto another. However, thinking of it as a transformation helps us think about when to use it.

> Use `.map` to "transform" an array of one thing into an array of something else.

I will rewrite the above type definition again in a slightly different format, using intention revealing names for the type parameters. Then we will look at some real-world examples.

```ts
Array<InputType>.map<OutputType>(
  callbackfn: (value: InputType) => OutputType
) => Array<OutputType>;
```

This is an identical type definition to the first one I showed you. I've included it because I know that seeing it like this will help some people grok it.

In the next scenario, we have an array of `Person` objects, and we want to map these into an array of `ReactNodes`:

```ts
type Person = {
  firstName: string;
  lastName: string;
  age: number;
};

type Props = { people: Person[] };

export const PeopleList: React.FC<Props> = ({people}) => (
  <ul>
    {people.map(
      (person) => (
        <li>{person.firstName} {person.lastName} | {person.age}</li>
      )
    )}
  </ul>
);
```

In this example, our map function transforms each `Person` object from the `people` array into a list item ReactNode. It's a simple use case and tightly coupled to the context we are using it -- the anonymous function we define for the map function isn't beneficial outside of the `PeopleList` component since it always returns a `<li/>` node.

Let's consider a use case where we might reuse our callback function in other contexts. In our next example, we are going to map an array of `People` into an array of full names:

```ts
type Person = {
  firstName: string;
  lastName: string;
  age: number;
};

function getFullnameFromPerson(person: Person): string {
  return `${person.firstName} ${person.lastName}`;
}

function listPeople(people: Person): string {
  return people.map(getFullnameFromPerson).join(', ');
}
```

In the above example `listPeople` passes the `getFullnameFromPerson ` function to `map`, but we could conceivably use `getFullnameFromPerson ` in other contexts too.

Let's try a more complicated example now, where we want to create a layer between our database implementation and our TypeScript implementation. There are several things `map` can help us with:

* In Postgres, we use `snake_case` column names, but in TypeScript and JavaScript, we use `camelCase`. We'll want to map between these types
* Enum types often come from another table; we'll map these too
* We'll also map our [64bit integer ids from strings](https://github.com/brianc/node-pg-types/issues/78) to [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)

**Javascript Types:**

```ts
type ArticleType = 'GENERAL' | 'REVIEW' | 'EDITORIAL';

type Article = {
  id: BigInt;
  articleType: ArticleType;
  author: string;
  title: string;
  urlSlug: string;
  content: string;
  publishedAt: Date;
}
```

**Database Types:**

```ts
type ArticleRow = {
  id: string; // Bigints come back from pg-node as a string
  article_type_id: number;
  author: string;
  title: string;
  url_slug: string;
  content: string;
  published_at: Date;
}

type ArticleTypeRow = {
  id: number; // small int
  type: string;
}
```

**Mapping:**

Now we have our cast of types, let's consider how we might map from these rather rough database types to these very usable javascript types. I'll write a function that selects the ten most recent articles, and we will use a series of maps to transform the query results.

```ts
const getRecentArticles = async (): Promise<Article[]> => {
  const result = await pool.query<ArticleRow>(`
    SELECT * FROM articles ORDER BY published_at LIMIT 10;
  `);
  return Promise.all(result.rows.map(getArticleFromArticleRow));
}

const getArticleFromArticleRow = async (row: ArticleRow): Promise<Article> => ({
  id: BigInt(row.id),
  articleType: await getArticleTypeById(row.article_type_id),
  author: row.author,
  title: row.title,
  urlSlug: row.url_slug,
  content: row.content,
  publishedAt: row.published_at,
});

/**
  This function memoises lookups to article type.
*/
const getArticleTypeById = (() => {
  let cache: Map<string, ArticleType>;
  return async (id: number): Promise<ArticleType> => {
    if (!cache) {
      const result = await pool.query<ArticleTypeRow>(`
        SELECT * FROM article_types
      `);
      cache = result.rows.reduce(
        (current, previous) => previous.set(current.id, current.type),
        new Map()
        // This gives us an object where we can lookup article types by id
      );
    }
    return cache.get(id);
})();
```

The first function in the above example, `getRecentArticles`, simply returns the ten most recently published articles. 

If you're wondering why we use `Promise.all` in the return statement, it is because our mapping callback function `getArticleFromArticleRow` returns a Promise. That means that the result of `result.rows.map(getArticleFromArticleRow)` is an array of promises. Still, the caller of `getRecentArticles` expects a single Promise containing an array of Articles.

`Promise.all` takes an array of promises and maps them to an array of resolved values within a single promise, thus fulfilling our contract to the caller of `getRecentArticles`.

Why did we need `getArticleFromArticleRow` to be asynchronous (return a promise)? Because we look up the article type from the article type enum table in Postgres. You are correct in thinking that a Postgres query for every row in `getRecentArticles` is wasteful and slow. That is why `getArticleTypeById` actually caches the value locally.

Looking at the implementation, we use an immediately invoked function expression to create a `cache` value protected in the closure of the function definition for `getArticleTypeById`. This closure means no other part of the application can access `cache`, but all calls to `getArticleTypeById` utilise the same cache. It is effectively a functional singleton, providing a memoised lookup, so we only have to query the database for `ArticleTypes` once per node process.

As you can see, `Array.prototype.map` is a powerfully simple concept. You can build complex transformations with it, create asynchronous processes, build recursive solutions, and even chunk and distribute a map across multiple processes because it is monadic. 

Now that we understand `map` let's see why `forEach` is different.

## Array.prototype.forEach

Let's start with a simplified TypeScript definition of `forEach` like we did with `map`:

```ts
forEach(
  callbackfn: (value: T) => void
): void;
```

This looks pretty similar to `map`, except that the callback function doesn't return anything, and neither does `forEach` itself. Why might we want a function that returns nothing for each element of an array? Because we want to create _side effects_.

> Use `forEach` for creating side-effects beyond the local scope

Let's understand side effects quickly by thinking about its opposite -- a pure function. Pure Functions always return the same value given the same inputs. The synchronous map functions in the previous example were pure, but the asynchronous functions accessing a database were impure because the return value could change between calls.

A function can also have a side effect when it affects part of the system beyond its input or output values. Let's move on to a concrete example.

If we wanted to send an email to every user in an array of users, we would use the `forEach` function if we do not care about the aggregate outcome of the email sending function. I'll give you an example, where our `handleMeetingBooked` function will email all attendees:

```ts
type Meeting = {
  name: string;
  attendees: User[];
  date: Date;
}

type User = {
  email: string;
  name: string;
}

const handleMeetingBooked = (meeting: Meeting): void => {
  const { attendees } = meeting;
  attendees.forEach((attendee) => {
    sendMeetingEmail(attendee, meeting);
  });
}

const sendMeetingEmail = (attendee: User, meeting: Meeting): void => {
  const emailBody = `Hi ${attendee.name},
    you have been invited to ${meeting.name} 
    on ${meeting.date} 
    with ${meeting.attendees.map(({name}) => name).join(', ')}`;
    sendEmail({ to: attendee.email, body: emailBody });
}
```

Notice how no part of `handleMeetingBooked` cares about what happens inside the callback function provided to `attendees.forEach`. We choose `forEach` instead of `map` because the behaviour is different and because it tells the readers of our code that the application shouldn't care about or depend upon the outcome of this email sending function.

Could I implement identical behaviour with `map`? Absolutely. Should I? _Absolutely not._ 

Let's look at a typical `forEach` code smell -- mutating an external collection from within the callback provided to `forEach`.

```ts
// BAD CODE, DO NOT DO THIS
const getUsersNames = (users: User[]): string[] => {
  const names = [];
  users.forEach((user) => {
    names.push(user.name);
  });
  return names;
}
```

What's wrong with the above code? Aside from a few unnecessary lines, it lies to the reader. It tells the person reading this code that the callback function won't change anything within the function, and then it mutates one of the function's variables! It mightn't seem so bad in a small function like this, but this is a recipe for bugs in larger functions.

> **Code smell:** Using `.push` inside `forEach`. Consider alternative e.g. `.map` or `.filter`

The correct implementation of the above would simply be:

```ts
const getUsersNames = (users: User[]): string[] => 
  users.map(({name}) => name);
```

Now that we have understood the difference between `map` and `forEach`, it is time to consider the role `reduce` plays in our code.

## Array.prototype.reduce

Reduce is the array function that seems to cause the most confusion. Rather than starting with the type definition, I will offer you my mental model of what reduce is for, and then we can look at the code and see how it supports this way of thinking.

> Use `reduce` to reduce a collection of elements to a single aggregate entity.

That is to say; I use reduce when I have many (an array) and want one (anything *but* an array). A typical example of this is summing the numbers in an array.

Let's take a look at a simplified version of the official type definition as we did before, this time keeping the goal of reducing down to a single aggregate entity in mind:

```ts
reduce<U>(
  callbackfn: (previousValue: U, currentValue: T) => U, 
  initialValue: U
): U;
```

The first thing to notice is that `reduce` returns a single `U` where `map` returned `U[]`. We can also see that we can supply an `initialValue`, but the type of this value must match the return type of the reduce function and that the callback function must also return this same type. 

I'll write this again with intention revealing names:

```ts
Array<InputType>.reduce<OutputType>(
  callbackfn: (previousValue: OutputType, currentValue: InputType) => OutputType, 
  initialValue: OutputType
): OutputType;
```

We'll reconsider the summation example, equipped with our new mental model and expanded type definition fresh in our minds:

```ts
const total = [1, 2, 3, 4, 5].reduce(
  (previous, current) => previous + current,
  0
);
console.log(total); // 15
```

Above, we started with a collection of numbers and wound up with just one number. We _reduced_ it down to its aggregate: the variable `total` with a value of `15`.

How else could we use this? Perhaps we want to reduce an array of users down to a count of common names:

```ts
type User = {
  firstName: string;
  lastName: string;
}

type CommonNamesAggregate = Record<string, number>;

const countCommonNames = (users: User[]): CommonNamesAggregate => {
  return users.reduce<CommonNamesAggregate>(
    (aggregate, user) => {
      const currentCount = aggregate[user.firstName];
      if (currentCount) {
        aggregate[user.firstName]++;
      } else {
        aggregate[user.firstName] = 1;
      }
      return aggregate;
    },
    {} // We create our new CommonNamesAggregate here
  );
```

In this case, our new aggregate entity is an object where each key is a first name from `users`, and the value is a count of the frequency of that name in the `users` array.

Let's write another flavour of the same `countCommonNames` solution and see if it helps us grok `reduce`:

```ts
const countCommonNames = (users: User[]): CommonNamesAggregate => {
  const uniqueNames = new Set(users.map(({firstName}) => firstName));
  const commonNamesAggregate = [...uniqueNames].reduce(
    (obj, name) => {
      obj[name] = 0;
      return obj;
    },
    {}
  );

  return users.reduce<CommonNamesAggregate>(
    (aggregate, user) => {
      aggregate[user.firstName]++;
      return aggregate;
    },
    commonNamesAggregate
  );
```

In this implementation, we first create the aggregate object separately and initialise its values to `0` by reducing our set of unique names, saving us from checking if the name already exists in our final reducer.

Now let's revisit `forEach` regarding the above code example. I'll implement the same behaviour using forEach, and then explain why it is a code smell.

```ts
// BAD CODE, DO NOT DO THIS
const countCommonNames = (users: User[]): CommonNamesAggregate => {
  const uniqueNames = new Set(users.map(({firstName}) => firstName));
  const commonNamesAggregate = [...uniqueNames].reduce(
    (obj, name) => {
      obj[name] = 0;
      return obj;
    },
    {}
  );

  users.forEach(
    (user) => commonNamesAggregate[user.firstName]++
  );

  return commonNamesAggregate;
};
```

This might look simpler because it has fewer lines of code, but it gives the reader less information about the intent of the code. By using `forEach`, we tell the reader to hold onto their hats and read carefully because we're about to do _something_. The issue is that the only clues about what that something is, come from code that is mainly outside and before the forEach callback.

> **Code smell:** Mutating a local variable from within a `forEach`.

Ultimately, forEach is the array function with the least semantic meaning and the least ability to reveal intentions. As such, forEach should be used as a last resort when there isn't a more suitable function or when you explicitly want to indicate that what occurs within the callback is not relevant to the adjacent code.

I also want to talk briefly about a code smell in `reduce` usage: returning an array from reduce.

> **Code smell:** Returning an array from reduce. Consider `map` or `filter` instead.

People usually do this by mistake when they want to chain `.filter` and `.map` together, but instead, they do it in one function. The damage from this anti-pattern is that we mislead readers of our code entirely.

## Conclusion

As programmers, we spend a lot of time working with collections of things, whether arrays, sets, maps, or objects. JavaScript comes with a fantastic suite of array functions that can make our days easier and our code more readable. Hopefully, this article has given you the confidence to know which array function to use and why.

#### TLDR

* Use `.map` to "transform" an array of one thing into an array of something else
* **Code smell:** Mutating a local variable from within a `map`.
* Use `forEach` for creating side-effects beyond the local scope
* **Code smell:** Using `.push` inside `forEach`. Consider alternative e.g. `.map` or `.filter`
* **Code smell:** Mutating a local variable from within a `forEach`.
* Use `reduce` to reduce a collection of elements to a single aggregate entity.
* **Code smell:** Returning an array from reduce. Consider `map` and/or `filter` instead.

