496

Lets say I have the following map:

let myMap = new Map().set('a', 1).set('b', 2);

And I want to obtain ['a', 'b'] based on the above. My current solution seems so long and horrible.

let myMap = new Map().set('a', 1).set('b', 2);
let keys = [];
for (let key of myMap)
  keys.push(key);
console.log(keys);

There must be a better way, no?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Lilleman
  • 7,392
  • 5
  • 27
  • 36

7 Answers7

876

Map.keys() returns a MapIterator object which can be converted to Array using Array.from:

let keys = Array.from( myMap.keys() );
// ["a", "b"]

EDIT: you can also convert iterable object to array using spread syntax

let keys =[ ...myMap.keys() ];
// ["a", "b"]
pawel
  • 35,827
  • 7
  • 56
  • 53
  • 7
    I like the use of the Spread Operator, though, my TypeScript transpiler throws `this.map.values().slice is not a function`. Maybe I should update. – Cody Aug 24 '17 at 22:57
  • 4
    @Cody that's because your `slice()` invocation is being executed before the spread operator. Try `[ ... Array.from(map.values()).slice(0) ]` – mgthomas99 Mar 06 '18 at 11:45
  • 6
    TypeScript 2.7.2 says for this: `const fooMap = new Map(); const fooArray = [...fooMap.keys()];` the following: TS2461: Type 'IterableIterator' is not an array type. So this is not allowed in TypeScript. Array.from works as expected. – Stefan Rein Apr 11 '18 at 11:45
  • @StefanRein Typescript spread operator looks he same, but is not equivalent to the ES6 spread, as it only works with Array and Object types, whereas ES6 works with any iterable. You can e.g. do `..."abc"` to get `["a","b","c"]` in ES6, which is not possible in TS. – pawel Apr 11 '18 at 12:04
  • @pawel `..."abc"` nor `...("abc")` are working in the chrome console, which supports ES6? – Stefan Rein Apr 11 '18 at 12:11
  • @StefanRein sorry, `[..."abc"]`, just like in the example with keys in my answer. – pawel Apr 11 '18 at 12:12
  • This doesn't work anymore, `map.keys()` will return an empty iterator. Instead, I'd suggest using `Objects.keys(map)`, which returns an array with the keys – dleal Jun 23 '21 at 15:25
  • @dleal Do you have a link to ECMA specification that introduces such a backward-incompatible, breaking change? If it is true it should be added to my answer. As of now, in my case, `Object.keys(map)` returns an empty array. – pawel Jun 25 '21 at 07:14
  • I might be mistaken, because in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys it appears as compatible. However, we're using JavaScript for our lambdas in AWS, and there the method is returning an empty array, with Object.keys() working – dleal Jun 25 '21 at 12:17
31

You can use the spread operator to convert Map.keys() iterator in an Array.

let myMap = new Map().set('a', 1).set('b', 2).set(983, true)
let keys = [...myMap.keys()]
console.log(keys)
Fernando Carvajal
  • 1,869
  • 20
  • 19
13

OK, let's go a bit more comprehensive and start with what's Map for those who don't know this feature in JavaScript... MDN says:

The Map object holds key-value pairs and remembers the original insertion order of the keys.
Any value (both objects and primitive values) may be used as either a key or a value.

As you mentioned, you can easily create an instance of Map using new keyword... In your case:

let myMap = new Map().set('a', 1).set('b', 2);

So let's see...

The way you mentioned is an OK way to do it, but yes, there are more concise ways to do that...

Map has many methods which you can use, like set() which you already used to assign the key values...

One of them is keys() which returns all the keys...

In your case, it will return:

MapIterator {"a", "b"}

and you easily convert them to an Array using ES6 ways, like spread operator...

const b = [...myMap.keys()];
Alireza
  • 100,211
  • 27
  • 269
  • 172
8

I need something similiar with angular reactive form:

let myMap = new Map().set(0, {status: 'VALID'}).set(1, {status: 'INVALID'});
let mapToArray = Array.from(myMap.values());
let isValid = mapToArray.every(x => x.status === 'VALID');
linfluX
  • 81
  • 1
  • 3
6

Not exactly best answer to question but this trick new Array(...someMap) saved me couple of times when I need both key and value to generate needed array. For example when there is need to create react components from Map object based on both key and value values.

  let map = new Map();
  map.set("1", 1);
  map.set("2", 2);
  console.log(new Array(...map).map(pairs => pairs[0])); -> ["1", "2"]
Tkoma
  • 81
  • 1
  • 5
1

Side note, if you are using a JavaScript object instead of a map, you can use Object.keys(object) which will return an array of the keys. Docs: link

Note that a JS object is different from a map and can't necessarily be used interchangeably!

Kevin L Xu
  • 158
  • 1
  • 2
  • 8
0

If You want extract keys and iterate over key map, You can do both operatrion in one line:

for (const i of Object.keys(myMap )) {
       console.log(i)
    }
sosnus
  • 968
  • 10
  • 28