javascript - get index in Map.prototype.forEach()

11,132

Solution 1

Map.prototype.forEach takes callback with two params: value and key.

No, it's invoked with three arguments, just like Array#forEach. The third is the map.

Is it possible to get the index of each entry, similar to Array.prototype.forEach(functcion(value, index) => {})

(Fairly sure the functcion part of that wasn't meant to be there.)

That's what key is. There is no separate "index."

Map's iteration order is defined for various iteration operations, but there's no direct iteration construct that gives you the index (as opposed to key) of an entry in that order. The order is original key insertion order, so for instance:

const m = new Map();
m.set("x", 1);
m.set("q", 2);
m.set("z"), 3);
m.set("x", "one");

If we loop through that map, we'll see the keys in the order the key was first added, so "x", "q", "z". Note that changing the value of the entry with the ke "x" didn't move it to the end.

Map#forEach (and for-of and anything else using the map's iterator) follows the iteration order, so if you're thinking of the index in terms of that order, you could track the index yourself:

const m = new Map();
m.set("one", "uno");
m.set("two", "due");
m.set("three", "tre");
let index = 0;
m.forEach((value, key) => {
  console.log(index++, key, value);
});

Alternately, you can get an array of the map entries (as [key, value] arrays):

let entries = Array.from(m.entries());

...and use the indexes of that array:

I'm just not sure what it buys you. :-)

Solution 2

I don't think so. Map object is a key/value map. To me at least the idea of an index doesn't make much sense, given that the order of the key/value pairs should not be guaranteed in a Map. If you are just curious how many pairs you have iterated on so far, you could just keep a counter that you mutate on each call, something like:

mapObj.forEach(() => {
    let i=0;
    return (key, value) => {
        // whatever you are doing with each key/value
        i += 1;
    }
});

It is not as elegant as an index as parameter, but would at least meet your needs.

Solution 3

The problem is defining what an index is. The spec defines a [[MapNextIndex]] internal slot of iterators, which stores the index of the [[MapData]] list which contains the next entry.

However, there is no way to access these indices. Even more, implementations are not required to internally behave like described by the spec, so they may not have these indices at all.

The data structures used in this Map objects specification is only intended to describe the required observable semantics of Map objects. It is not intended to be a viable implementation model.

What you can do is use an index which says how many times you have to call next() on a new iterator in order to get that entry. These kind of indexes are not absolute, and may become obsolete if you alter the map. Just initialize a counter to 0, and increase it at each iteration.

{
  let i = 0;
  for(var [key,value] of map) {
    // Do something
    ++i;
  }
}
Share:
11,132
eagor
Author by

eagor

Updated on June 13, 2022

Comments

  • eagor
    eagor almost 2 years

    Map.prototype.forEach takes callback with two params: value and key. Is it possible to get the index of each entry, similar to Array.prototype.forEach((value, index) => {})

  • just-boris
    just-boris almost 8 years
    If you don't like side-effects, like index variable, you can use map.keys() and get index from there m.forEach((value, key) => console.log(Array.from(m.keys()).indexOf(key)))
  • T.J. Crowder
    T.J. Crowder almost 8 years
    @just-boris: No, the only order guarantee I can find in the specification is only in relation to Map#forEach, not iteration order generally, so in theory keys and entries could be in a different order. (I find this odd, that the order would be specified for one operation only [rather than all or none], but I looked pretty carefully.) Unless you can point to something in the spec (as opposed to MDN) that I missed (he said hopefully)?
  • just-boris
    just-boris almost 8 years
    You are right, I just showed how to get a number from key without extra counter. Order doesn't matter, but every key will get a unique number, right?
  • T.J. Crowder
    T.J. Crowder almost 8 years
    @just-boris: But not necessarily a reliable one; per spec, the keys could be returned in any order at all by keys. The closest thing to order Maps have is the original-key-insertion-order guarantee on forEach. (Which is a good reason not to rely on the order, but... :-) )
  • T.J. Crowder
    T.J. Crowder almost 6 years
    @DotBot - Since the functcion appears in the question and I'm quoting it, removing it isn't appropriate.
  • Shaya
    Shaya almost 6 years
    My bad @T.J.Crowder, us programmers and our OCD eyes ;)