JavaScript list (array) comprehension explained with examples
Published: 2023-05-13 18:37:13

> ✋ **Update:** This post was originally published on my blog [decodingweb.dev](https://www.decodingweb.dev), where you can read the [latest version](https://www.decodingweb.dev/javascript-list-comprehension) for a 💯 user experience. _~reza_

[List comprehension](https://brilliant.org/wiki/list-comprehension) is a programming technique to create a new list based on an existing list. This quick guide explores three approaches to JavaScript list comprehension (a.k.a array comprehension).

Programmers use list or array comprehension for two reasons:

1. To create a subset of a broad list (based on a set of conditions)
2. To transform list items into new values and store them as a new list.

List comprehension is usually available as a language construct in many programming languages like Python or Perl. However, JavaScript has its own way of array comprehension.

For instance, if you have a list of email addresses and need to filter out Gmail accounts, here’s how you’d do it in Python:

```python
emails = [
    # ...
]

gmail_accounts = [email for email in emails if email.endswith('gmail.com')]
```

In JavaScript, however, there's no list (in this case, an array) comprehension construct, but there are ways to do that, thanks to `Array.prototype.map()`, `Array.prototype.filter()`, and the spread operator (`...`).

Let's see how.

## List (array) comprehension in JavaScript

Based on your use cases, there are several approaches to list comprehension in JavaScript:

1. <u>Using Array.prototype.map()</u>
2. <u>Using Array.prototype.filter()</u>
3. <u>Using the for...of statement</u>

## 1. Using Array.prototype.map()

If you want to transform every item in an array and return it as a new array, the `Array.prototype.map()` method is what you need.

Let's see some examples.

**Example #1:** Imagine we have a list of decimal values (e.g., prices) but only want the integer part (without the fraction part.)

```javascript
const prices = [12.5, 45.34, 12.5, 9.5]
const pricesAsInt = prices.map(price => Math.trunc(price))

console.log(pricesAsInt)
// output: (4) [12, 45, 12, 9]
```

In the above example, we called the `map()` method with an arrow function, which returns an integer (by using `Math.trunc()`)

**Example #2:** Sometimes you have an array of objects and want to create a list of `x` from each object. Consider the following variable:

```javascript
const persons = [
  { id: 238, name: Alice },
  { id: 874, name: Bob },
  { id: 421, name: Charlie }
]
```

Imagine you want to generate an array of names from the above data structure. Here's how you'd do it with the `map()` method:

```javascript
const names = persons.map(p => p.name)

console.log(names)
// output: (3) ['Alice', 'Bob', 'Charlie']
```

In the above example, we pass the short arrow function `p => p.name` to the `map()` method. The variable `p` refers to an object on each iteration.

**Example #3:** Considering the `persons` array, imagine you need to add a new entry (`active: true`) to each object.

Here's one way of doing it:

```javascript
const activePersons = persons.map(p => {
    p.active = true
    return p
})
```

If you want an elegant single-line statement:

```javascript
const activePersons = persons.map(p => ({ ...p, active: true }))
```

Since we're using an arrow function without the curly braces, we wrapped the return value in a pair of parentheses. Otherwise, it would be mistaken for a function boundary by the parser. We used the spread operator (`...p`) to pack the existing entries alongside `active: true` as the new object.

As a rule of thumb, use the `map()` method whenever you want to transform list items without filtering them.

## 2. Using Array.prototype.filter()

If you want to create a subset of the original list. That's where the `Array.prototype.filter()` method comes in handy.

Let's see some examples.

**Example #1:** Imagine you have a list of emails but want to filter only Gmail accounts:

```javascript
const emailAddresses = [
// ...
]

const gmailAccounts = emailAddress.filter(i => i.endsWidth('gmail.com'))
```

The filter method accepts a callback function, which is run against every item in the array; If the callback function returns `true`, the item will be included in the new subset array.

In the above example, if the current item ends with `gmail.com`, the callback returns `true`, and the item is included in the final result.

**Example #2:** You can use the `filter()` method in conjunction with `map()`. 

For instance, after filtering the Gmail accounts, if you want to store them as objects in an array, you can do it like so:

```javascript
const gmailAccounts = emailAddress.filter(i => i.endsWidth('gmail.com'))
    .map(i => ({ email: i }))
```
As simple as that.

## 3. Using the for...of statement

The `for...of` statement allows you to iterate over a series of values from an iterable object (e.g., Array, String, TypedArray, Map, Set, NodeList, etc.). 

If you want to reimplement the Gmail accounts example with the `for...of` statement, you could do it like so:

```javascript
const emailAddresses = []
const gmailAccounts = []

for (email of emails) {
    if (!email.endsWith('gmail.com')) {
        return
    }
    
    gmailAccounts.push(email)
}
```

In the above example, we iterate over the items and skip any email address not ending with `gmail.com`. Otherwise, we push it to the `gmailAccounts` array.

You could also use the `Array.prototype.forEach()` method to create a new list in the same way as the `for...of` statement.

Alright, I think it does it! I hope you found this quick guide helpful.

Thanks for reading.