Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Map.prototype.map

Map.prototype.map ( callbackfn [ , thisArg ] )

The map method calls MapTransform abstract operation passing this value as M, callbackfn, thisArg as is, and false as updateKey.

The length property of the map method is 1.

Map.prototype.mapEntries ( callbackfn [ , thisArg ] )

The mapEntries method calls MapTransform abstract operation passing this value as M, callbackfn, thisArg as is, and true as updateKey.

The length property of the mapEntries method is 1.

Helper abstract operations

MapTransform(M, callbackfn, updateKey, [, thisArg ] ) abstract operation

NOTE callbackfn should be a function that accepts three arguments. MapTransform calls callbackfn once for each key/value pair present in the map object, in key insertion order, and returns a new transformed map. callbackfn is called only for keys of the map which actually exist; it is not called for keys that have been deleted from the map.

If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. If it is not provided, undefined is used instead.

callbackfn is called with three arguments: the value of the item, the key of the item, and the Map object being traversed.

MapTransform does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.

When the MapTransform method is called, the following steps are taken:

  1. If Type(M) is not Object, then throw a TypeError exception.
  2. If M does not have a [[MapData]] internal slot throw a TypeError exception.
  3. If M's [[MapData]] internal slot is undefined, then throw a TypeError exception.
  4. If IsCallable(callbackfn) is false, throw a TypeError exception.
  5. If thisArg was supplied, let T be thisArg; else let T be undefined.
  6. Let entries be the List that is the value of M's [[MapData]] internal slot.
  7. Let resultMap be the result of calling InstanceFromInstanceCreate abstract operation, passing M as object, null as parameters list, and true for throw.
  8. Repeat for each Record {[[key]], [[value]]} e that is an element of entries, in original key insertion order
  • If e.[[key]] is not empty, then
    • Let funcResult be the result of calling the [[Call]] internal method of callbackfn with T as thisArgument and a List containing e.[[value]], e.[[key]], and M as argumentsList.
    • ReturnIfAbrupt(funcResult).
    • If updateKey is true, then
      • If funcResult is not an exotic Array object, then throw a TypeError
      • Let newKey be Get(funcResult, 0)
      • Let newValue be Get(funcResult, 1)
    • Else,
      • Let newKey be e.[[key]]
      • Let newValue be the funcResult
    • Call resultMap's explicit set method passing newKey as key, and newValue as value.
  1. Return resultMap.

InstanceFromInstanceCreate(O, argumentsList, throw) Abstract Operation

When the InstanceFromInstanceCreate abstract operation is called with an instance object O, argumentsList, and throw boolean parameter, the following steps are taken:

  1. Let C be Get(O, "constructor").
  2. ReturnIfAbrupt(C).
  3. Let newInstance be undefined
  4. If IsConstructor(C) is false, then
  5. If throw is true, throw TypeError exception.
  6. Else, 1. Let thisRealm be the running execution context’s Realm. 2. If SameValue(thisRealm, GetFunctionRealm(C)) is true, then
    1. If argumentsList is null, let argumentsList be an empty List.
    2. Let newInstance be the result of calling the [[Construct]] internal method of C with the argumentsList as an argument list.
  7. Return newInstance.
@rwaldron

This comment has been minimized.

Copy link

@rwaldron rwaldron commented Oct 2, 2014

Why can't keys be changed as well? A map operation on a map should expect return [key, value], which would allow changing of key

@arv

This comment has been minimized.

Copy link

@arv arv commented Oct 2, 2014

8 needs to use this.constructor so that sub classing works as expected.

@DmitrySoshnikov

This comment has been minimized.

Copy link
Owner Author

@DmitrySoshnikov DmitrySoshnikov commented Oct 3, 2014

@rwaldron, depends on how we'd like this method to behave. Probably we want to forbid keys changing, or to have a separate method mapWithKey or something, since in general cases users likely will map only values, and forcing them to always return an array can be both: annoying (to always return unchanged key itself) and having an useless array allocation. OTOH, since we accept the key as an argument, we may assume we'd like to return it (probably transformed) as well. Will think about it.

@arv, yeah, it's true, thanks.

Also, I thought about 9.1.3 - "Call resultMap's set explicit method" is not safe since the set method can be overridden. We'll just need an abstract operation MapSet(map, key, value) that both, this algorithm and originial Map.prototype.set will reuse.

@leebyron

This comment has been minimized.

Copy link

@leebyron leebyron commented Oct 6, 2014

This looks great.

Strongly support having the mapper function provided to map return values. This means mapper functions can be shared between Arrays, Maps and Sets.

mapKeys could be defined similar, but flipping the first two arguments:

var uppercaseKeys = lowercaseKeys.mapKeys((key, value, collection) => key.toUppercase());

Allowing you to use the same mapper functions for mapKeys that you would for map.


I think mapKeys is a clearer name than mapWithKeys for a couple reasons. First: terseness. Second: reduce confusion with overloaded vernacular. "Map" means both a type of collection (noun) and an operation (verb). "Map with keys" could easily be misunderstood as referring to a new thing containing keys whereas "map keys" is more obviously using "map" as a verb.


If there is real-world demand for mapper functions which can alter both key and value, I would propose this API:

map.mapEntries(([key, value], index, map) => [key, value])

It shares the property of the other map* methods that the return type is expected to be the same as the type of the first argument in the mapper function.

@DmitrySoshnikov

This comment has been minimized.

Copy link
Owner Author

@DmitrySoshnikov DmitrySoshnikov commented Oct 6, 2014

@leebyron, thanks, I actually like the map.mapEntries, pretty intuitive. And we can assume that map.map is for values only.

I'll prepare an updated spec to address all comments from above a bit later.

@ivan-kleshnin

This comment has been minimized.

Copy link

@ivan-kleshnin ivan-kleshnin commented Mar 16, 2017

Why can't keys be changed as well? A map operation on a map should expect return [key, value], which would allow changing of key

Because map is an isomorphism and must map values (key-value pairs in this case) one-to-one.
If you need filtering (or key changing) – you need either flatMap or reduce.

@L3P3

This comment has been minimized.

Copy link

@L3P3 L3P3 commented Feb 23, 2021

I reaaally like the map, mapKeys and mapEntries proposals!
I might be a bit foolish but why is the last message from 2017 and the proposal not realized yet?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment