# .map() equivalent for objects - Object.mapEntries()

**URL:** <https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643>\
**Category:** 💡 Ideas\
**Created:** [February 11, 2021, 5:28am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643 "2021-02-11T05:28:38Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 11, 2021, 5:28am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/1 "2021-02-11T05:28:38Z")

</div>

I'm proposing we add the method `Object.mapEntries()` into the language, to allow one to map one object to another object. This is a very common task, and it would make a lot of code just a little more readable if there was a canonical way to do so.

It could be defined as follows:

```javascript
Object.mapEntries = (obj, mapFn) => {
  const entries = Object.entries(obj)
  return Object.fromEntries(entries.map(mapFn))
}

```

Example usage:

```javascript
const users = {
  NKpiZ: { name: 'Sarah', age: 5 },
  UaAFu: { name: 'Samuel', age: 10 },
  avTRz: { name: 'Samantha', age: 15 },
}

const userIdToAge = Object.mapEntries(users, ([id, { age }]) => [id, age])
// userIdToAge is { NKpiZ: 5, UaAFu: 10, avTRz: 15 }
const nameToAge = Object.mapEntries(users, ([, { name, age }]) => [name, age])
// nameToAge is { Sarah: 5, Samuel: 10, Samantha: 15 }
const nameToId = Object.mapEntries(users, ([id, { name, age }]) => [name, id])
// nameToId is { Sarah: 'NKpiZ', Samuel: 'UaAFu', Samantha: 'avTRz' }

```

We could potentially add `Object.mapKeys()` and `Object.mapValues()` too, but mapEntries() alone would be a big win.

---

<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:** [February 11, 2021, 6:39am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/2 "2021-02-11T06:39:22Z")

</div>

Would be nice alternative to the ‘reduce pattern’ I’ve seen a bit.

```javascript
toEntries(obj).reduce((out, ([key, value]) => {
  out[key] = change(value);
  return out;
}, {})

```

FromEntries is also another case where I’ve wanted the pipeline operator:

```javascript
toEntries(obj)
  .filter(([key]) => condition(key))
  .map(([key, value]) => [key, change(value)])
  .filter(([_, value]) => condition(value))
  |> fromEntries;

```

---

<div class="post-metadata">

**Author:** ![markm](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/markm/32/19_2.png) [@markm](https://es.discourse.group/u/markm)\
**Post date:** [February 11, 2021, 5:40pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/3 "2021-02-11T17:40:25Z")

</div>

See the comments at [agoric-sdk/objArrayConversion.js at 4890dcdbee725d15c1656f3882c6d49aa030778e · Agoric/agoric-sdk · GitHub](https://github.com/Agoric/agoric-sdk/blob/4890dcdbee725d15c1656f3882c6d49aa030778e/packages/zoe/src/objArrayConversion.js#L44)

The comments are on essentially the same code. Once I started explaining it well enough for it to be used reliably, I was surprised at how much I needed to say.

I purposely chose to do `entries.map(entry => mapFn(entry))` rather than `entries.map(mapFn)` because in the latter, the binding of other parameters of `mapFn` beyond the first doesn't make any sense as part of the meaning of `mapEntries`.

I support this abstraction. In writing that explanation, I came to appreciate all the possible variations and ways to get this wrong. So standardizing on this one would be good.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 11, 2021, 6:25pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/4 "2021-02-11T18:25:31Z")

</div>

Good point @markm, some of those other parameters wouldn't be needed by the mapping function. Certainly, an index shouldn't be provided (yes, object ordering is stable, but we probably shouldn't encourage code to depend on the order of the object's entries). However, it might be good to pass in the original object as a second parameter? Same way array.map() passes in the original array as the third parameter? I've personally had never found that to be a useful parameter, but I assume others have.

```javascript
Object.mapEntries = (obj, mapFn) => {
  const entries = Object.entries(obj)
  return Object.fromEntries(entries.map(entry => mapFn(entry, obj)))
}

```

---

<div class="post-metadata">

**Author:** ![markm](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/markm/32/19_2.png) [@markm](https://es.discourse.group/u/markm)\
**Post date:** [February 11, 2021, 7:52pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/5 "2021-02-11T19:52:54Z")

</div>

Since we don't have a good analogy for the second parameter and would therefore omit it, we no longer have a least-surprise reason for the original-object parameter. Other than least-surprise by symmetry, I do not find any other reason for this extra parameter compelling. We should omit it.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 11, 2021, 9:50pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/6 "2021-02-11T21:50:48Z")

</div>

Sounds good to me - unless someone can think of a compelling use case for the original object reference parameter, it makes sense to just leave it out.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 14, 2021, 12:07am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/7 "2021-02-14T00:07:47Z")

</div>

Feedback seems to be generally positive, so I went ahead and threw together a proposal repo [here](https://github.com/theScottyJam/proposal-map-object-entries) to help aid further discussion.

As I was researching how other languages and libraries handled this issue, I found that most of them did so by only providing a mapValues() function, or both mapValues() and mapKeys(). None of the languages/libraries I looked at allowed modifying both the key and value at the same time.

I still think `Object.mapEntries()` would be an intuitive and powerful tool in the javascript ecosystem, especially since we already have a notion of what an "entry" means, because of functions such as `Object.entries()` or `Object.fromEntries()`. But, there's certainly room for discussion here on whether we want to pursue the original idea of having `Object.mapEntries()`, or if we should have an implementation similar to other languages/libraries and have both `Object.mapKeys()` and `Object.mapValues()`, or do some combination of both. (I also outlines these thoughts in the github repo, under the "Comparison" heading).

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [February 14, 2021, 12:54am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/8 "2021-02-14T00:54:55Z")

</div>

Another alternative could be just “map” to avoid asks for keys/values - or, map with an optional second argument that defaults to “entries” but can also be “keys” or “values”.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 14, 2021, 1:17am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/9 "2021-02-14T01:17:57Z")

</div>

When you say

> map with an optional second argument that defaults to “entries” but can also be “keys” or “values”.

Are you referring to using a literal string as a second argument to control the behavior of the mapping function? i.e. `Object.map(obj, entries => newEntries)` and `Object.map(obj, 'keys', key => newKey)`. I don't think any ECMAScript API uses string sentinels like that.

If we do choose to just have a single `Object.mapValues()` function, I think it would make sense to just give it your suggested name of `Object.map()` instead.

---

<div class="post-metadata">

**Author:** ![bergus](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bergus/32/152_2.png) [@bergus](https://es.discourse.group/u/bergus)\
**Post date:** [February 14, 2021, 2:07am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/10 "2021-02-14T02:07:42Z")

</div>

This has been proposed before and was rejected by the commitee. Please see [GitHub - tc39/proposal-object-iteration: ECMA TC39 proposal for making mapping over Objects more concise](https://github.com/tc39/proposal-object-iteration#october-2019) and its issues for existing discussion.

---

<div class="post-metadata">

**Author:** ![ljharb](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/ljharb/32/8_2.png) [@ljharb](https://es.discourse.group/u/ljharb)\
**Post date:** [February 14, 2021, 2:13am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/11 "2021-02-14T02:13:38Z")

</div>

Ah right, third argument then :-)

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [February 14, 2021, 4:06am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/12 "2021-02-14T04:06:04Z")

</div>

Ah man, I guess this is a lost cause then :(

I don't think they'll ever get people to start using the `Map()` in place of normal objects all the time - there's so much syntax support around normal objects that won't ever exist with `Map()`. (not that `Map()` isn't useless, it's plenty useful, it's just more verbose to use).

I do see the concern about it being a slippery slope (assuming they're talking about a slippery slope of adding other functions such as `Object.filter()`) - I was a little worried about the same thing.

---

<div class="post-metadata">

**Author:** ![claudiameadows](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/claudiameadows/32/126_2.png) [@claudiameadows](https://es.discourse.group/u/claudiameadows)\
**Post date:** [February 15, 2021, 5:44pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/13 "2021-02-15T17:44:08Z")

</div>

I don't see how `Object.mapEntries(obj, fn)` is any better than `Object.fromEntries(Object.entries(obj).map(fn))` except for a mild perf boost.

I do see the value in `Object.mapValues(obj, fn)`, though - engines could make it as fast as `Array.prototype.map` if not faster as they can just reuse the existing object's type info.

---

<div class="post-metadata">

**Author:** ![brandonmcconnell](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/brandonmcconnell/32/2395_2.png) [@brandonmcconnell](https://es.discourse.group/u/brandonmcconnell)\
**Post date:** [March 31, 2025, 11:21pm UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/14 "2025-03-31T23:21:48Z")

</div>

@theScottyJam @bergus That proposal rejection ends with "More investigation to follow."

I have seen this pattern **`Object.fromEntries(Object.entries(obj).map(fn))`** scattered across almost every repo I've touched, easily thousands of times. Maps are helpful, but there is no escaping the use of plain objects, especially as they are JSON-safe (tons of value-add already right there).

A simple **`Object.mapEntries(obj, fn)`**

Benefits:

- some perf boost, as @claudiameadows mentioned
- much easier to type
- easier to understand at a glance

This is not so dissimilar to when TC39 opted to add `Array.prototype.at(N)`. We could already achieve identical functionality via `.slice(N)[0]`, which is hardly more complex, and yet this small quality-of-life improvement has made index-based references for arrays drastically simpler.

Here is just one simple example, and even here, I can easily feel the weight of the change, more in practice than in observation:

```js
// before
Object.fromEntries(
  Object.entries(changes).map(([key, { current }]) => {
    return [key, current];
  })
)

// after
Object.mapEntries(changes, ([key, { current }]) => {
  return [key, current];
})

```

Possibly the biggest selling point of all— I'm confident, however unfoundedly, that at least 50% of the times developers use `fromEntries`, they use it to support this exact pattern.

`fromEntries` and `entries` are crucial as they are the foundational modular pieces that make this pattern achievable without greater abstraction. However, in terms of usage, `mapEntries` might actually be more widely used than either of these other two combined.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 1, 2025, 1:10am UTC](https://es.discourse.group/t/map-equivalent-for-objects-object-mapentries/643/15 "2025-04-01T01:10:09Z")

</div>

You would also need/want `flatMapEntries` I would think in case the desired transform isn't 1:1
