# Array.prototype.mapNotNull

**URL:** <https://es.discourse.group/t/array-prototype-mapnotnull/2441>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [October 4, 2025, 1:39pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441 "2025-10-04T13:39:08Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 4, 2025, 1:39pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/1 "2025-10-04T13:39:08Z")

</div>

Transforming arrays while filtering out null/undefined values with a single call is a common pattern in modern languages, but JavaScript requires chaining, reduce or for loops:

```javascript
const users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: null },
  { id: 3, name: "Charlie", email: "charlie@example.com" },
  { id: 4, name: "David", email: undefined }
];

// Option 1: Chaining
const emails = users
  .map(user => user.email)
  .filter(email => email != null);

// Option 2: Imperative reduce
const emails = users.reduce((acc, user) => {
  if (user.email != null) acc.push(user.email);
  return acc;
}, []);

// Option 3: For Loop
const emails = [];
for (const user of users) {
  if (user.email != null) {
    emails.push(user.email);
  }
}

```

Proposed Solution

```javascript
const emails = users.mapNotNull(user => user.email);
// ["alice@example.com", "charlie@example.com"]

```

Benefits:

- Single pass, no intermediate array
- Clear declarative intent
- Better performance characteristics (depends, not really the main benefit)

This pattern is common across languages:

- **Kotlin** : `mapNotNull` - [docs](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map-not-null.html)

```bash
  users.mapNotNull { it.email }

```

- **Swift** : `compactMap` - [docs](https://developer.apple.com/documentation/swift/sequence/compactmap(_:))

```swift
  users.compactMap { $0.email }

```

- **Rust** : `filter_map` - [docs](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map)

```bash
  users.iter().filter_map(|u| u.email.clone())

```

---

<div class="post-metadata">

**Author:** ![senocular](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/senocular/32/642_2.png) [@senocular](https://es.discourse.group/u/senocular)\
**Post date:** [October 4, 2025, 2:35pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/2 "2025-10-04T14:35:23Z")

</div>

flatMap is another option

```typescript
const emails = users.flatMap(user => user.email ?? []);

```

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 4, 2025, 11:03pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/3 "2025-10-04T23:03:36Z")

</div>

I always forget this one. Personally it’s intent is not clear so I avoid, but that is totally personal preference 😀

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 6, 2025, 4:44pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/4 "2025-10-06T16:44:59Z")

</div>

This operation is usually called `filterMap`.

Unfortunately I think it’s unlikely we’re going to get any new operations on `Array.prototype` given the [previous difficulties](https://developer.chrome.com/blog/smooshgate).

That said, if we _were_ going to do this, I would want it to support null or undefined return values (which are after all just another kind of value). Languages like [Rust](https://doc.rust-lang.org/std/iter/struct.FilterMap.html) accomplish this with a `Result` type, which is a lot less practical in JS (we could require every value be wrapped, but that’s a lot of extra objects). The way I’d do this is to have an optional second parameter where returning that value indicates “skip”. The default value would of course be `undefined`. So `[0, 1, 2].filterMap(x => { if (x === 1) return; return x + 1; })` would still work, but if you wanted to filter an array which might validly contain `undefined` you could do `let sigil = {}; collection.filterMap(x => predicate(x) ? sigil : mapper(x), sigil)`.

Unfortunately this conflicts with the notion of the second parameter being the `this`, which is how it works for the other array prototype methods.

---

<div class="post-metadata">

**Author:** ![Josh-Cena](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/josh-cena/32/1408_2.png) [@Josh-Cena](https://es.discourse.group/u/Josh-Cena)\
**Post date:** [October 6, 2025, 7:20pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/5 "2025-10-06T19:20:07Z")

</div>

`reduce` and `reduceRight` also have a second parameter that’s not `this`, and arguably `sort` as well

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 7, 2025, 12:21pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/6 "2025-10-07T12:21:57Z")

</div>

Thank you for the reply.

You raise good points about flexibility. Interestingly, neither Kotlin nor Swift have a general-purpose `filterMap` - they both specifically have methods for the null-filtering case:

- Kotlin: `mapNotNull` (no general filterMap)
- Swift: `compactMap` (no general filterMap)
- Rust: `filter_map` (the exception - it's generalised using the Option type)

For other `filter+map` Combinations, Kotlin and Swift rely on chaining. This suggests the null-filtering case is special enough to warrant its own method. The 80/20 rule applies - filtering null/undefined is by far the most common use case. We could start with `mapNotNull` that fits a widespread use case that more modern languages are supporting.

As for `Array.prototype` additions being difficult - `flat()` and `flatMap()` were successfully added despite smooshgate. The TC39 staging process would identify any compatibility issues early

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 7, 2025, 12:57pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/7 "2025-10-07T12:57:12Z")

</div>

> [@AlexanderKaran](#):
>
> As for `Array.prototype` additions being difficult - `flat()` and `flatMap()` were successfully added despite smooshgate. The TC39 staging process would identify any compatibility issues early

`flat` was only added after browsers shipped under other names and broke real websites, whose users then complained. They have stated their unwillingness to go through that again. There is nothing magical about the staging process which allows identifying compatibility issues without breaking actual websites and finding out the hard way.

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 7, 2025, 1:13pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/8 "2025-10-07T13:13:31Z")

</div>

Fair, but it does still give time for feedback. You're right that the staging process alone isn't a magic solution.

Some further thoughts:

- `mapNotNull` is a compound name (less likely to conflict than single words like `flatten`)
- We could do some GitHub searches for `Array.prototype` extensions
- Most JS libraries call this operation `compact` (Lodash) or use chaining
- Possibly dig through the HTTP Archive (never done this before, not sure if possible)

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 7, 2025, 5:12pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/9 "2025-10-07T17:12:27Z")

</div>

Incidentally, looking at other languages:

- [Kotlin](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/map-not-null.html) has `mapNotNull` as mentioned
- [Swift](https://developer.apple.com/documentation/swift/sequence/compactmap(_:)) has `compactMap` as mentioned
- [Rust](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) has `filter_map` with support for arbitrary values
- [Haskell](https://hackage.haskell.org/package/base-4.21.0.0/docs/Data-Maybe.html#v:mapMaybe) has `mapMaybe` with support for arbitrary values
- [Elm](https://package.elm-lang.org/packages/elm/core/latest/List#filterMap) has `filterMap` with support for arbitrary values
- [Ruby](https://ruby-doc.org/core-2.7.2/Enumerable.html#method-i-filter_map) has `filter_map` with support only for truthy values
- [OCaml](https://ocaml.org/manual/5.3/api/List.html#VALfilter_map) has `filter_map` with support for arbitrary values
- [PureScript](https://pursuit.purescript.org/packages/purescript-arrays/4.0.1/docs/Data.Array#v:mapMaybe) has `mapMaybe` with support for arbitrary values
- [F#](https://fsharp.github.io/fsharp-core-docs/reference/fsharp-collections-listmodule.html#choose) has `choose` with support for arbitrary values
- [Erlang](https://www.erlang.org/doc/apps/stdlib/lists.html#filtermap/2) has `filtermap` with support for arbitrary values
- [Racket](https://docs.racket-lang.org/reference/pairs.html#(def._((lib._racket%252Flist..rkt)._filter-map))) has `filter-map` with support only for non-`#false` values
- [Crystal](https://crystal-lang.org/api/1.17.1/Enumerable.html#compact_map(%2526%253AT-%3E_)-instance-method) has `compact_map` with support for only non-nil values
- [StandardML](https://smlfamily.github.io/Basis/list.html#SIG:LIST.mapPartial:VAL) (yes, really!) has `mapPartial` with support for arbitrary values
- [Elixir](https://hexdocs.pm/elixir/1.1.1/Enum.html#filter_map/3) _used_ to have `filter_map` with support for arbitrary values, but deprecated it
  - also it was kind of a different operation because it took a predicate and a mapper separately

---

<div class="post-metadata">

**Author:** ![jschoi](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/jschoi/32/917_2.png) [@jschoi](https://es.discourse.group/u/jschoi)\
**Post date:** [October 9, 2025, 11:15pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/10 "2025-10-09T23:15:50Z")

</div>

I have run into this use case before. It is quite annoying to have to reach for the solutions described in the original post.

I note that the pipe operator proposal, currently in stasis, would make it more ergonomic/fluent to add convenience APIs like these as static functions rather than prototype methods, e.g., `arr |> Array.filterMap(##, f, sigil)`. But that is far away from advancing further for now.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [October 11, 2025, 8:35am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/11 "2025-10-11T08:35:37Z")

</div>

Great analysis of other languages! So putting the languages with a first class Option/Maybe type (Rust, Haskell, Elm, OCaml, PureScript, F#, StandardML) to one side.

The rough patterns we have are:

- Map ignoring nil/null `<T>(v => T) => Array<Exclude<T, null>>`
  - Kotlin, Swift, Crystal
  - If the method ignored `null` and was named `mapNotNull` like Kotlin, this feels clearly named and widely applicable. When code wants to keep `null` the existing patterns are still available for this (niche?) case.

- Map ignore false/falsey `<T>(v => T) => Array<Exclude<T, false>>`
  - Ruby, Racket
  - Ignoring falsey feels much too broad
  - A method that ignores `false` feels less widely applicable than ignoring `null`

- Tuple as Maybe `<T>(v => false | [true, T]) => Array<T>`
  - Erlang
  - If one benefit of the method is to be more efficent than `.map().filter()`, returning lots of small arrays feels contradictory to that goal

- Combined Map/Filter `<T>(v => T, T => boolean) => Array<T>`
  - Elixier (deprecated it)
  - Seems most flexible but doesn't match other languages and doesn't match other array methods

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 11, 2025, 8:55am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/12 "2025-10-11T08:55:12Z")

</div>

I guess when I originally put the post together, my primary focus was `Map` ignoring `undefined` and `null`. It is such a typical pattern to map and filter `null` values. After working closely with Kotlin for the last two years, I have found its existence a delight. Something I would love to put an official proposal in for and find a champion.

However, I can also see the value of the combined mapFilter, which would make `mapNotNull` pointless.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 11, 2025, 2:54pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/13 "2025-10-11T14:54:00Z")

</div>

I continue to think the right design is to take an optional second parameter as a sigil for a value to ignore, which if not passed would default to `undefined`. This gives you support for arbitrary values but doesn’t require allocating a wrapper Result object (something which is free in languages like Rust but is definitely not free here), and for the common case where ignoring `undefined` is fine you can just not pass that value.

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 12, 2025, 1:52am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/14 "2025-10-12T01:52:16Z")

</div>

So something like so:

```javascript
const users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: null },
  { id: 3, name: "Charlie", email: "charlie@example.com" },
  { id: 4, name: "David", email: undefined }
];

const emails1 = users.filterMap(
  user => user.email,
  null                 
);

```

I wonder if a function would be better than a value, which gives more flexibility.

```javascript
const emails2 = users.filterMap(
  user => user.email,
  email => email != null
);

```

Then we come back to the name issue:

List of repos that have extended `Array.prototype` with `filterMap`, 19 in total. Would need to do more digging but `filterMap` may be possible.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 12, 2025, 2:28am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/15 "2025-10-12T02:28:27Z")

</div>

> [@AlexanderKaran](#):
>
> I wonder if a function would be better than a value, which gives more flexibility.

No, that doesn’t actually give more flexibility (less, actually, since it can depend only on the output of the mapper and not the input), and is both slower and harder to read.

For your example, if you want to filter out both `null` and `undefined` you can do `user => user.email ?? undefined` and not bother with the second argument at all. Or we could special-case `undefined` mean either, I guess, though I don’t think this actually comes up much.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [October 12, 2025, 2:28am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/16 "2025-10-12T02:28:55Z")

</div>

> [@AlexanderKaran](#):
>
> I wonder if a function would be better than a value, which gives more flexibility.

While this provides the most flexibility, I think in practice this is more flexibility than is needed. It's almost always going to be the case that it's ignoring a single static value. So the function is adding unnecessary overhead.

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 12, 2025, 2:51am UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/17 "2025-10-12T02:51:38Z")

</div>

Great so:

```javascript
const users = [
  { id: 1, name: "Alice", email: "alice@example.com" },
  { id: 2, name: "Bob", email: null },
  { id: 3, name: "Charlie", email: "charlie@example.com" },
  { id: 4, name: "David", email: undefined }
];

const emails1 = users.filterMap(
  user => user.email,
  null                 
);

```

I will put together a proposal following the template and paste the link back here to see if anyone wants to pick it up. Anything you think would be worthwhile to include in the doc?

---

<div class="post-metadata">

**Author:** ![AlexanderKaran](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/alexanderkaran/32/2541_2.png) [@AlexanderKaran](https://es.discourse.group/u/AlexanderKaran)\
**Post date:** [October 13, 2025, 1:24pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/18 "2025-10-13T13:24:06Z")

</div>

@bakkot @aclaymore I have started working on a rough outline of the proposal here [GitHub - AlexanderKaran/Array.prototype.filterMap: ECMAScript Proposal and spec for adding a `filterMap` function to `Array.prototype`](https://github.com/AlexanderKaran/Array.prototype.filterMap).

This is just a draft, lots more to add. Do you think it will be possible to find a champion for this?

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [October 14, 2025, 5:45pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/19 "2025-10-14T17:45:02Z")

</div>

Even after letting it sit in my mind for a few days I am still gravitating towards `mapNotNull`.

Being able to chose the single ignored value feels very niche to me. Maybe I would sometimes want to filter out `0` or `NaN` from a calculation but I could do that by mapping those values to `null`.

I can't think of a case where I would want to preserve `null` in an array that I have just mapped. Maybe if I knew the indexes of the result would still line up with another list and I'm going to `zip` the `null` to something, but that seems like code that would be hard to read.

If the ignored value can be selected, I'm not sure what the natural name for such a method would be. `mapFilter` feels wrong as `filter` chooses what to keep, maybe `mapExclude`?

We could research to look for as many use cases as possible, I'm happy to be wrong that there are many cases where ignoring `null` (or `undefined`) wouldn't work well.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [October 14, 2025, 7:18pm UTC](https://es.discourse.group/t/array-prototype-mapnotnull/2441/20 "2025-10-14T19:18:16Z")

</div>

> [@aclaymore](#):
>
> I can't think of a case where I would want to preserve `null` in an array that I have just mapped.

I think it’s pretty easy to come up with such examples - say, you have a time series of sensor readings, and you want to pick out those from a particular sensor and get the value, where a value of `null` indicates no reading; the natural way to do this is `list.filterMap(x => x.id === id ? x.value : NO_VAL, NO_VAL)`.

But I also want to push back on the general line of thinking. We’re making a general-purpose programming language. It should be general purpose. Assuming that a particular kind of value will never arise is how you end up making it impossible to have an export named `.then`, or to `.find` over a list using a predicate where `undefined` might validly satisfy the predicate us (I can’t tell you how many times I’ve done that and then had to switch to `findIndex`, but it’s enough for this to be a sore point).

> [@aclaymore](#):
>
> If the ignored value can be selected, I'm not sure what the natural name for such a method would be.

`filterMap`, like it is in most other languages. Actually it should be `filterMap` regardless of whether the ignored value can be selected, because that’s the name almost everyone is going to look for it under.

[Next page](https://es.discourse.group/t/array-prototype-mapnotnull/2441.md?page=2)
