Briefly
Map
— a collection for storing data of any type in the form of pairs [key
, meaning each value is stored with a unique key that is then used to access that value. Moreover, values of any type are also accepted as keys.
Main methods for working with the Map
collection:
set
— sets the value;( key , value ) get
— returns the value;( key ) has
— checks for the presence of the provided key;( key ) values
— returns an iterator of all values in the collection;( ) keys
— returns an iterator of all keys in the collection;( ) entries
— returns an iterator of pairs( ) [key
;, value ] delete
— removes a specific value;( key ) clear
— completely clears the collection;( ) for
— iterates over the keys and values of the collection.Each ( callback )
Contains the size
property to get the number of values in the collection.
Example
const someData = new Map()someData.set('1', 'Value under string key 1')someData.set(1, 'Value under numeric key 1')someData.set(true, 'Value under boolean key true')console.log(someData.size)// 3console.log(someData.get(1))// Value under numeric key 1console.log(someData.get('1'))// Value under string key 1console.log(someData.has(true))// truesomeData.clear()console.log(someData.size)// 0
const someData = new Map() someData.set('1', 'Value under string key 1') someData.set(1, 'Value under numeric key 1') someData.set(true, 'Value under boolean key true') console.log(someData.size) // 3 console.log(someData.get(1)) // Value under numeric key 1 console.log(someData.get('1')) // Value under string key 1 console.log(someData.has(true)) // true someData.clear() console.log(someData.size) // 0
How to Understand
Creating a Collection
A collection is created using the constructor. You can create an empty Map
:
const map = new Map()console.log(map.size)// 0
const map = new Map() console.log(map.size) // 0
Or you can immediately pass initial values. For this, an array consisting of other arrays must be passed to the constructor. These arrays should consist of two elements: the first element — the key, and the second — the value:
const map = new Map([['js', 'JavaScript'], ['css', 'Cascading Style Sheets']])console.log(map.size)// 2console.log(map.get('js'))// JavaScript
const map = new Map([['js', 'JavaScript'], ['css', 'Cascading Style Sheets']]) console.log(map.size) // 2 console.log(map.get('js')) // JavaScript
Working with the Collection
Map
provides a small set of convenient methods for working with data.
To store a value in the collection, you need to use the set
method. The first argument is the key, and the second is the value:
const map = new Map()map.set('js', 'JavaScript')
const map = new Map() map.set('js', 'JavaScript')
You can retrieve a value using the get
method. The only argument is the key whose data you want to get. If there is no value in the collection for the provided key, get
will return undefined
.
const map = new Map()map.set('js', 'JavaScript')console.log(map.get('js'))// JavaScript
const map = new Map() map.set('js', 'JavaScript') console.log(map.get('js')) // JavaScript
You can check if there is a value in the collection with a specific key using the has
method:
const map = new Map()map.set('js', 'JavaScript')console.log(map.has('js'))// trueconsole.log(map.has('css'))// false
const map = new Map() map.set('js', 'JavaScript') console.log(map.has('js')) // true console.log(map.has('css')) // false
You can remove a specific value using the delete
method, which also accepts the key as an argument. delete
returns true
if the element for the provided key existed and was removed. The clear
method completely clears the collection:
const map = new Map()map.set('html', 'HTML')map.set('css', 'CSS')map.set('js', 'JavaScript')console.log(map.size)// 3map.delete('css')console.log(map.size)// 2map.clear()console.log(map.size)// 0
const map = new Map() map.set('html', 'HTML') map.set('css', 'CSS') map.set('js', 'JavaScript') console.log(map.size) // 3 map.delete('css') console.log(map.size) // 2 map.clear() console.log(map.size) // 0
Iterating Over Values
Map
provides a built-in iterator for iterating over values:
const map = new Map()map.set('html', 'HTML')map.set('css', 'CSS')map.set('js', 'JavaScript')for (let [key, value] of map) { console.log(`${key} — ${value}`)}// html — HTML// css — CSS// js — JavaScript
const map = new Map() map.set('html', 'HTML') map.set('css', 'CSS') map.set('js', 'JavaScript') for (let [key, value] of map) { console.log(`${key} — ${value}`) } // html — HTML // css — CSS // js — JavaScript
You can also do the same using the for
method:
const map = new Map()map.set('html', 'HTML')map.set('css', 'CSS')map.set('js', 'JavaScript')map.forEach((value, key) => { console.log(`${key} — ${value}`)})// html — HTML// css — CSS// js — JavaScript
const map = new Map() map.set('html', 'HTML') map.set('css', 'CSS') map.set('js', 'JavaScript') map.forEach((value, key) => { console.log(`${key} — ${value}`) }) // html — HTML // css — CSS // js — JavaScript
When iterating over values, Map
always outputs them in the order they were added.
Differences from Objects
Regular objects can also be used for storing data. However, keys in them can only be strings or symbols:
const obj = { 1: 'String', '2': 'Number', true:'Bool',}console.log(Object.keys(obj))// [ '1', '2', 'true' ]
const obj = { 1: 'String', '2': 'Number', true:'Bool', } console.log(Object.keys(obj)) // [ '1', '2', 'true' ]
Map
, on the other hand, allows any value to be used as a key: an object, function, primitive values, and even null
, undefined
and NaN
. The SameValueZero algorithm is used to compare keys.
How the SameValueZero Algorithm Works
Briefly. The SameValueZero algorithm works like strict comparison using =
with the single distinction: for SameValueZero, NaN
equals NaN
. This is why NaN
can be used as keys in Map
— we can find such a key through simple comparison.
Details. The SameValueZero algorithm for comparing variables x
and y
according to the specification:
- If the types of
x
andy
differ, return false. Possible types:Undefined
,Null
,Boolean
,String
,Number
,Big
,Int Object
orSymbol
. Do not confuse with the result of thetypeof
operator. - If the type of
x
andy
is Number:- if the value of
x
is NaN and the value ofy
is NaN, return true; - if the value of
x
is -0 and the value ofy
is +0, return true; - if the value of
x
is +0 and the value ofy
is -0, return true; - return true if the value of
x
is equal to the value ofy
, otherwise return false.
- if the value of
- If the type of
x
andy
isBig
, return true if the value ofInt x
equals the value ofy
. Otherwise, return false. - If the type of
x
andy
isUndefined
, return true. - If the type of
x
andy
isNull
, return true. - If the type of
x
andy
isString
, return true ifx
andy
are the same sequences of characters (same length and the same character codes at corresponding indexes). Otherwise, return false. - If the type of
x
andy
isBoolean
, return true if both values ofx
andy
are true or both values ofx
andy
are false. Otherwise, return false. - If the type of
x
andy
is Symbol, return true ifx
andy
are the same symbol. Otherwise, return false. - If the types of
x
andy
inherit fromObject
, return true ifx
andy
reference the same object. Otherwise, return false.
const func = (name) => `Hello, ${name}`const obj = { foo: 'bar' }const map = new Map()map.set(func, 'value func')map.set(obj, 'value object')map.set(undefined, 'value undefined')map.set(NaN, 'value NaN')map.set(null, 'value null')console.log(map.get(func))// value funcconsole.log(map.get(obj))// value objectconsole.log(map.get(undefined))// value undefinedconsole.log(map.get(NaN))// value NaNconsole.log(map.get(null))// value null
const func = (name) => `Hello, ${name}` const obj = { foo: 'bar' } const map = new Map() map.set(func, 'value func') map.set(obj, 'value object') map.set(undefined, 'value undefined') map.set(NaN, 'value NaN') map.set(null, 'value null') console.log(map.get(func)) // value func console.log(map.get(obj)) // value object console.log(map.get(undefined)) // value undefined console.log(map.get(NaN)) // value NaN console.log(map.get(null)) // value null
When using SameValueZero to compare keys, type coercion does not occur. Therefore, a number and its string representation will be two different keys:
const map = new Map()map.set(1, 'numeric 1')map.set('1', 'string 1')console.log(map.size)// 2console.log(map.get(1))// numeric 1console.log(map.get('1'))// string 1
const map = new Map() map.set(1, 'numeric 1') map.set('1', 'string 1') console.log(map.size) // 2 console.log(map.get(1)) // numeric 1 console.log(map.get('1')) // string 1
When using non-primitive types as keys, remember that they are stored by reference, so to access the key set with an object, you need to pass the same object.
Let’s create two variables that point to the same object, and use them as keys in Map
:
const dataObject = { position: 'left' }const sameObject = dataObjectconsole.log(dataObject === sameObject)// trueconst map = new Map()map.set(dataObject, 'Value for dataObject')map.set(sameObject, 'Value for sameObject')console.log(map.size)// 1console.log(map.get(dataObject))// Value for sameObjectconsole.log(map.get(sameObject))// Value for sameObject
const dataObject = { position: 'left' } const sameObject = dataObject console.log(dataObject === sameObject) // true const map = new Map() map.set(dataObject, 'Value for dataObject') map.set(sameObject, 'Value for sameObject') console.log(map.size) // 1 console.log(map.get(dataObject)) // Value for sameObject console.log(map.get(sameObject)) // Value for sameObject
However, if we take two separate objects with the same content, we will get two different keys:
const playerOne = { position: 'left' }const playerTwo = { position: 'left' }console.log(playerOne === playerTwo)// falseconst map = new Map()map.set(playerOne, 'Player 1')map.set(playerTwo, 'Player 2')console.log(map.size)// 2console.log(map.get(playerOne))// Player 1console.log(map.get(playerTwo))// Player 2
const playerOne = { position: 'left' } const playerTwo = { position: 'left' } console.log(playerOne === playerTwo) // false const map = new Map() map.set(playerOne, 'Player 1') map.set(playerTwo, 'Player 2') console.log(map.size) // 2 console.log(map.get(playerOne)) // Player 1 console.log(map.get(playerTwo)) // Player 2