JavaScript (ES2026) Module 8 – Map, Set and weak collections
Map: keys of any type and the ES2026 upsert methods
How a JavaScript Map stores keys of any type in insertion order, when to choose it over a plain object, and how the ES2026 getOrInsert methods work.
What you will learn
- Use a Map for lookups whose keys come from data, are not strings or change often
- Explain how a Map compares keys, including NaN, -0, objects and arrays
- Iterate a Map in insertion order and convert it to and from objects, entries and JSON
- Count, group and cache with getOrInsert and getOrInsertComputed, with a fallback for older engines
Before you start
What you’ll learn · Before you start
What you will learn
- Use a Map for lookups whose keys come from data, are not strings or change often
- Explain how a Map compares keys, including NaN, -0, objects and arrays
- Iterate a Map in insertion order and convert it to and from objects, entries and JSON
- Count, group and cache with getOrInsert and getOrInsertComputed, with a fallback for older engines
Before you start
On this page
A Map is JavaScript’s built-in dictionary: it stores pairs of a key and a value, and the key can be any value at all,
a number, an object or even NaN, not only a string. It remembers the order in which its keys were added, reports
how many entries it holds in size, and has a method for each job: get, set, has, delete and
clear. Choose it over a plain object when the keys come from data rather than from your code, when they are not
strings, or when entries come and go all the time. ES2026 adds two methods for the chore every Map user repeats,
“give me the value for this key, and store a default first if it is missing”: getOrInsert takes the default as a
value, and getOrInsertComputed takes a function that runs only when the key is missing. This lesson runs each of
them and gives their exact rules, including the cases the method names do not tell you.
The everyday methods
new Map() takes nothing, or any iterable of [key, value] pairs, such as an array of two-item arrays:
// PIN codes are numbers, so they make good Map keys as they are.
const offices = new Map([
[411001, 'Pune GPO'],
[110001, 'New Delhi GPO'],
]);
offices.set(560001, 'Bengaluru GPO').set(600001, 'Chennai GPO');
console.log('get 110001:', offices.get(110001));
console.log('get 400001:', offices.get(400001));
console.log('has 560001:', offices.has(560001), ' has "560001":', offices.has('560001'));
console.log('size:', offices.size);
console.log('delete 600001:', offices.delete(600001), ' again:', offices.delete(600001));
offices.clear();
console.log('size after clear:', offices.size); Output
get 110001: New Delhi GPO get 400001: undefined has 560001: true has "560001": false size: 4 delete 600001: true again: false size after clear: 0
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node map-basics.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
set(key, value)adds the key or replaces its value, and returns the Map itself, which is why twosetcalls can be chained on one line.get(key)returns the value, orundefinedwhen the key is missing. It cannot tell a missing key from a key whose value isundefined;has(key)can.has(key)compares keys exactly, so the number 560001 and the string'560001'are different keys. A PIN code read from a form field is a string, and looking it up in a Map of numbers fails until you convert it.sizeis a property you read, not a method you call.delete(key)returnstrueif it removed an entry andfalseif there was nothing to remove, andclear()removes every entry.
Keys of any type
A plain object can only have strings and symbols as property names, so every other key is converted to a string on the way in. A Map keeps each key exactly as it was given:
// The same four keys, stored in a plain object and in a Map.
const asObject = {};
const asMap = new Map();
for (const key of [1, '1', true, 'true']) {
asObject[key] = typeof key;
asMap.set(key, typeof key);
}
console.log('object keys:', Object.keys(asObject));
console.log('map keys:', [...asMap.keys()], 'size', asMap.size);
// Objects and arrays are keys by identity: the same object, not an equal-looking one.
const seat = [3, 14];
const booked = new Map([[seat, 'Asha']]);
console.log('same array:', booked.get(seat), ' new [3, 14]:', booked.get([3, 14]));
// NaN finds itself, and -0 is stored as 0 (SameValueZero).
const readings = new Map([[NaN, 'sensor offline']]);
readings.set(-0, 'zero');
console.log('get NaN:', readings.get(NaN), ' get 0:', readings.get(0));
console.log('stored key is -0:', Object.is([...readings.keys()][1], -0)); Output
object keys: [ '1', 'true' ] map keys: [ 1, '1', true, 'true' ] size 4 same array: Asha new [3, 14]: undefined get NaN: sensor offline get 0: zero stored key is -0: false
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node key-types.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
The object ends up with two properties, because 1 and '1' both became '1', and true and 'true' both became
'true'; the Map keeps all four keys, each with its own type. Three more rules decide which keys count as the same, and all three
come from SameValueZero, the comparison the standard prescribes for Map keys:
- Objects and arrays are keys by identity. The Map finds the
seatarray it was given, but a new[3, 14]is a different array, however alike it looks. To look things up by content, build a key from the content, such as the string'3-14', with a separator that cannot occur inside the parts. NaNfinds itself, althoughNaN === NaNisfalse. A sensor that reportsNaNcan be a key like any other.-0is stored as0. Before a key is compared or stored, the standard’sCanonicalizeKeyedCollectionKeystep turns-0into+0, soget(0)finds the entry and the stored key is not-0.
Equality: ==, === and Object.is compares SameValueZero with the other three ways JavaScript has of asking whether two values are the same.
Insertion order and iteration
An ordinary object does not list its keys in the order you wrote them: keys that look like array indexes come first, in ascending numeric order, and only then the other strings, in insertion order, as Object.keys, entries, fromEntries and key order shows. A Map has one rule for every key:
// An object lists integer-like keys first, in ascending order; a Map keeps insertion order for every key.
const asObject = { 10: 'Ravi', 2: 'Meena', b: 'Kiran', a: 'Asha' };
const asMap = new Map([[10, 'Ravi'], [2, 'Meena'], ['b', 'Kiran'], ['a', 'Asha']]);
console.log('object:', Object.keys(asObject).join(' '));
console.log('map: ', [...asMap.keys()].join(' '));
asMap.set(2, 'Meena K'); // an existing key: the value changes, the position stays
asMap.delete(10);
asMap.set(10, 'Ravi'); // deleted, then set again: it goes to the end
for (const [key, value] of asMap) console.log(key, '->', value);
// forEach passes the value first, then the key.
asMap.forEach((value, key) => console.log(`forEach(value = ${value}, key = ${key})`)); Output
object: 2 10 b a map: 10 2 b a 2 -> Meena K b -> Kiran a -> Asha 10 -> Ravi forEach(value = Meena K, key = 2) forEach(value = Kiran, key = b) forEach(value = Asha, key = a) forEach(value = Ravi, key = 10)
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node insertion-order.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
The standard describes a Map as a list of entries, and that picture explains every line of the output:
How the standard describes a Map's entries
Text description of the diagram
A column of five boxes shows the entries of a Map of PIN codes, from top to bottom in the order their keys were added.
- Entry 1 holds the key 411001 with the value 'Pune GPO'.
- Entry 2 holds 110001 with 'New Delhi GPO'. A note says that `set(110001, …)`, a key the Map already has, changes the value in this entry.
- Entry 3 is empty, drawn with a dashed border: its key, 600001, was deleted. Loops skip it, and no entry moves up to fill the gap.
- Entry 4 holds 560001 with 'Bengaluru GPO'.
- The last box, dashed, is where `set(400001, …)` adds its new entry: a new key always goes after all the others.
A line under the boxes says that `for…of`, `keys()`, `values()` and `forEach` visit the entries from 1 to 4.
- Setting a key the Map already has changes the value in its entry, so
2keeps its place afterset(2, 'Meena K'). - Deleting a key empties its entry, and setting it again creates a new entry at the end, which is why
10moved from first to last. for…ofwalks the entries in order and gives a[key, value]array each time, ready to destructure.keys(),values()andentries()return iterators over the keys, the values or the pairs.forEachpasses the value first and the key second, the reverse of the pairs thatfor…ofgives. Mixing the two up is a common source of bugs that silently swap keys and values.
Because a loop walks the entries by position, changing the Map while you loop is defined behaviour: an entry deleted before the loop reaches it is skipped, and an entry added during the loop is visited too. That also means a loop that adds a new key on every step never ends.
Converting to and from objects, arrays and JSON
Maps and objects convert into each other through arrays of pairs, and JSON needs a deliberate step:
const fares = { sleeper: 455, '3A': 1210, '2A': 1745 };
// Object → Map: Object.entries gives [key, value] pairs, which is what new Map takes.
const fareMap = new Map(Object.entries(fares));
console.log('get 3A:', fareMap.get('3A'));
// Map → array of entries, and back to an object.
console.log('entries:', JSON.stringify([...fareMap]));
console.log('object:', JSON.stringify(Object.fromEntries(fareMap)));
// JSON.stringify sees a Map as an object with no own properties.
console.log('stringify(map):', JSON.stringify(fareMap));
// Object.fromEntries turns every key into a string, so 1 and '1' collide.
const mixed = new Map([[1, 'number one'], ['1', 'string one']]);
console.log('map size:', mixed.size, ' object:', JSON.stringify(Object.fromEntries(mixed)));
// Map.groupBy (ES2024) builds a Map of arrays from a list.
const payments = [
{ id: 'p1', status: 'success', amount: 250 },
{ id: 'p2', status: 'failed', amount: 99 },
{ id: 'p3', status: 'success', amount: 1200 },
];
const byStatus = Map.groupBy(payments, (p) => p.status);
for (const [status, list] of byStatus) console.log(status, list.map((p) => p.id).join(', ')); Output
get 3A: 1210
entries: [["sleeper",455],["3A",1210],["2A",1745]]
object: {"sleeper":455,"3A":1210,"2A":1745}
stringify(map): {}
map size: 2 object: {"1":"string one"}
success p1, p3
failed p2
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node map-conversions.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
new Map(Object.entries(obj))turns an object’s own enumerable string keys into a Map, andObject.fromEntries(map)turns a Map back into an object.[...map](orArray.from(map)) gives the array of[key, value]pairs, which also works withsort,filterand the other array methods.JSON.stringify(map)gives"{}". A Map keeps its entries in an internal slot, not in properties, so the JSON has none of them, and there is no error to warn you. To send a Map as JSON, send[...map], orObject.fromEntries(map)when every key is a string.Object.fromEntriesturns every key into a string, so distinct keys of a Map can collide: the number 1 and the string'1'became one property, and the later value won.Map.groupBy(items, fn)(ES2024) sorts a list into a Map of arrays, keyed by whateverfnreturns; unlikeObject.groupBy, the keys keep their type.
The last lesson of this module, Choosing between objects, arrays, Map and Set, shows how to send Maps and Sets through JSON and get them back.
getOrInsert: one call instead of has, set and get
Code that collects values under a key repeats the same three steps: check whether the key is there, store an empty
starting value if it is not, then read the value back. getOrInsert(key, value) does all three: if the Map has the
key, it returns the stored value and changes nothing; otherwise it stores value under the key and returns it:
// getOrInsert is new in ES2026. Where an engine lacks it (Node.js 24, Chrome before 145), the helper does the same steps.
function getOrInsert(map, key, value) {
if (typeof map.getOrInsert === 'function') return map.getOrInsert(key, value);
if (!map.has(key)) map.set(key, value);
return map.get(key);
}
const cities = ['Pune', 'Delhi', 'Pune', 'Kochi', 'Delhi', 'Pune'];
// Counting: a number cannot change in place, so the new count is stored with set.
const counts = new Map();
for (const city of cities) counts.set(city, getOrInsert(counts, city, 0) + 1);
console.log('counts:', [...counts].map(([city, n]) => `${city} ${n}`).join(', '));
// Grouping: getOrInsert returns the array that is stored, and push changes it in place.
const orders = [['Pune', 'A-101'], ['Delhi', 'B-7'], ['Pune', 'A-102']];
const byCity = new Map();
for (const [city, order] of orders) getOrInsert(byCity, city, []).push(order);
console.log('by city:', JSON.stringify([...byCity]));
// The same grouping without getOrInsert takes has, set and get.
const byHand = new Map();
for (const [city, order] of orders) {
if (!byHand.has(city)) byHand.set(city, []);
byHand.get(city).push(order);
}
console.log('same result by hand:', JSON.stringify([...byHand]) === JSON.stringify([...byCity]));
console.log('getOrInsert built in:', typeof Map.prototype.getOrInsert === 'function'); Output
counts: Pune 3, Delhi 2, Kochi 1 by city: [["Pune",["A-101","A-102"]],["Delhi",["B-7"]]] same result by hand: true getOrInsert built in: true
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node word-count.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
The two halves of the example show where it helps:
- Grouping, when the value is an array or an object that you change in place:
getOrInsert(city, []).push(order)returns the array that is in the Map, new or old, andpushchanges it there. Four lines ofhas,setandgetbecome one. - Counting, when the value is a number: a number cannot change in place, so the new count still has to be stored
with
set. The example’sgetOrInsert(counts, city, 0) + 1is just another way of writing(counts.get(city) ?? 0) + 1. - An existing value is never replaced, even when it is
undefined:getOrInsertdecides by whether the key is there, not by what is stored under it.
Where getOrInsert works
getOrInsert and getOrInsertComputed are part of ES2026. MDN’s compatibility data lists them in Chrome 145,
Firefox 144, Safari 26.2, Node.js 26 and Deno 2.6.7, so this lesson’s examples run on Node.js 26.10.0; the QuickJS
engine behind the Run buttons has them too. Node.js 24 and older browsers do not, and calling them there throws a
TypeError. The getOrInsert helper at the top of the example works everywhere: it uses the built-in method when
there is one and the same three steps when there is not.
getOrInsertComputed: a default made only when needed
JavaScript evaluates every argument before it calls a function, so getOrInsert(word, []) builds a new empty array
on every call, even when the key is there and the array is thrown away at once. getOrInsertComputed(key, fn) takes
a function instead and calls it only when the key is missing:
// getOrInsert evaluates its default on every call; getOrInsertComputed calls its function only for a missing key.
let made = 0;
const newBucket = () => {
made += 1;
return [];
};
const eager = new Map();
for (const word of ['chai', 'chai', 'chai']) eager.getOrInsert(word, newBucket()).push(word);
console.log('getOrInsert: arrays made', made, ' kept', eager.size);
made = 0;
const lazy = new Map();
for (const word of ['chai', 'chai', 'chai']) lazy.getOrInsertComputed(word, newBucket).push(word);
console.log('getOrInsertComputed: arrays made', made, ' kept', lazy.size);
// The function receives the key, so one function serves every key.
const slugs = new Map();
const slugify = (title) => title.toLowerCase().replaceAll(' ', '-');
console.log(slugs.getOrInsertComputed('Masala Dosa', slugify), slugs.getOrInsertComputed('Masala Dosa', () => 'not called'));
// The key is canonicalised first (-0 becomes 0) and is the only argument; this is undefined.
const zeros = new Map();
zeros.getOrInsertComputed(-0, function (key, ...rest) {
console.log('key is -0:', Object.is(key, -0), ' more arguments:', rest.length, ' this:', this);
return 'zero';
});
// If the function stores the same key itself, its return value replaces what it stored.
const notes = new Map();
const value = notes.getOrInsertComputed('k', (key) => {
notes.set(key, 'stored inside');
return 'returned';
});
console.log('returned:', value, ' stored:', notes.get('k'), ' size:', notes.size);
// The function must be callable even when the key is already there.
try {
new Map([['k', 1]]).getOrInsertComputed('k', 'not a function');
} catch (error) {
console.log('a non-function throws', error.name);
} Output
getOrInsert: arrays made 3 kept 1 getOrInsertComputed: arrays made 1 kept 1 masala-dosa masala-dosa key is -0: false more arguments: 0 this: undefined returned: returned stored: returned size: 1 a non-function throws TypeError
Recorded with Node.js 26.10.0 on macOS 26 arm64. To run it yourself: mise exec node@26.10.0 -- node computed-once.mjs
Runs on this device, in your browser. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs.
Your run, in this browser
For three copies of one word, getOrInsert built three arrays and kept one; getOrInsertComputed built one. With
millions of keys, or a default that is expensive to make (a parsed template, a database handle, a Set per user),
that difference is real work saved. The function receives the key, so one function such as slugify can serve every
key. The standard spells out four details, and the example checks each of them:
- The second argument must be a function, even when it is not needed. A string throws a
TypeErroralthough the key is there, so a mistake shows up on the first call rather than on the first miss. - The key is canonicalised first:
-0becomes0before the function sees it, exactly as forset. - The function gets one argument, the key, and
thisisundefined, so a method passed on its own loses its object, as it does with any callback. - If the function stores the same key itself, its return value wins. After the function returns, the Map looks for the key again and overwrites the value it finds, so there is never a second entry for one key. Code that relies on this is hard to read: keep the function from changing the same Map.
Map or plain object?
| Criterion | Plain object | Map |
|---|---|---|
| Key types | Strings and symbols; any other key becomes a string | Any value, kept as it is |
| Keys present at the start | Inherited names such as constructor; __proto__ is special | None |
| Order | Index-like keys first, then insertion order | Insertion order for every key |
| Number of entries | Object.keys(obj).length builds an array | map.size |
| JSON | Written and read as it is | Needs [...map] or Object.fromEntries |
| Fixed field names known in the code | Natural: obj.price | Awkward: map.get('price') |
| When to choose | Records with known fields, and JSON data | Dictionaries whose keys come from data |
A plain object is the right shape for a record, a thing with a fixed set of named fields such as
{ id, name, price }, and for data that travels as JSON. A Map is the right shape for a dictionary, where the keys are
values that arrive at run time: user ids, words in a text, PIN codes, objects. The keys of an object also include names
it inherits, so 'constructor' in {} is true, and assigning to the key '__proto__' calls a setter instead of
storing a value: harmless when you write the keys yourself, a bug when users choose them. A Map starts empty and stores
any key as data. How fast each one is depends on the engine, the size and the operation; the last lesson of this
module measures both instead of guessing.
Exercise: words by frequency
Exercise · Medium · JavaScript
Count words into a Map, most frequent first
A search box logs the words people type, and the team wants to see which words come up most. Write frequencies(words), which counts how often each string appears in the array words and returns a Map from each word to its count:
- the entries come in order of count, the highest count first;
- words with the same count come in ascending order of their code units, the order that
<gives for strings (so'B'comes before'a', and'10'before'2'); - every string counts as it is:
'Chai'and'chai'are two different words; - an empty array gives an empty
Map, and the array you are given is not changed.
frequencies(['chai', 'dosa', 'chai']) // Map(2) { 'chai' => 2, 'dosa' => 1 }
frequencies(['pune', 'agra', 'agra', 'pune']) // Map(2) { 'agra' => 2, 'pune' => 2 }
The starter code counts correctly but returns the words in the order it first met them: run the sample tests to see which ones that fails. The engine of the Run button has getOrInsert, so you may use it.
Starter code · frequencies.mjs
/**
* Counts how often each string appears in words. Returns a Map from word to count, the highest count first, and words
* with equal counts in ascending code-unit order.
*/
export function frequencies(words) {
const counts = new Map();
for (const word of words) counts.set(word, (counts.get(word) ?? 0) + 1);
// Replace this line: the entries are still in the order the words first appeared.
return counts;
} The sample tests · frequencies.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { frequencies } from './frequencies.mjs';
test('counts every word', () => {
assert.deepEqual([...frequencies(['chai', 'dosa', 'chai'])], [['chai', 2], ['dosa', 1]]);
assert.deepEqual([...frequencies(['idli'])], [['idli', 1]]);
});
test('the highest count comes first', () => {
assert.deepEqual([...frequencies(['a', 'b', 'b', 'c', 'c', 'c'])], [['c', 3], ['b', 2], ['a', 1]]);
assert.deepEqual([...frequencies(['x', 'y', 'y', 'x', 'y'])], [['y', 3], ['x', 2]]);
});
test('equal counts are in code-unit order', () => {
assert.deepEqual([...frequencies(['pune', 'delhi', 'agra'])], [['agra', 1], ['delhi', 1], ['pune', 1]]);
assert.deepEqual([...frequencies(['b', 'a', 'B'])], [['B', 1], ['a', 1], ['b', 1]]);
assert.deepEqual([...frequencies(['2', '10', '1', '2', '10'])], [['10', 2], ['2', 2], ['1', 1]]);
});
test('words that are names of object properties count like any other', () => {
const words = ['constructor', '__proto__', 'toString', '__proto__'];
assert.deepEqual([...frequencies(words)], [['__proto__', 2], ['constructor', 1], ['toString', 1]]);
});
test('an empty array gives an empty Map', () => {
const result = frequencies([]);
assert.ok(result instanceof Map, 'the result is a Map');
assert.equal(result.size, 0);
});
test('the input array is not changed', () => {
const words = ['tea', 'coffee', 'tea'];
frequencies(words);
assert.deepEqual(words, ['tea', 'coffee', 'tea']);
}); A hint
Count first, then sort: [...counts] gives an array of [word, count] pairs. Sort it with a comparison that looks at the counts first (b[1] - a[1] puts the larger count first) and, only when the counts are equal, compares the words with < and >. new Map(pairs) keeps the order of the array it is given.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Self-check
Check yourself
6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
Interview questions
When do you choose a Map over a plain object for key–value data? Choose a Map when the keys come from data rather
than from your code. A Map takes keys of any type and keeps them apart, so 1 and '1' are two keys and an object
is a key by identity, while a plain object turns every key into a string. A Map starts empty, whereas an object
inherits names such as constructor and treats __proto__ specially, which matters as soon as users choose the keys.
A Map also keeps every key in insertion order and reports its size directly. Choose a plain object for records whose
field names you know in advance, such as { id, name, price }, and for data that goes to and from JSON as it is,
since JSON.stringify writes a Map as {}.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
A log processor groups 5 million lines by user id with if (!byUser.has(id)) byUser.set(id, []); byUser.get(id).push(line). A pull request replaces that with byUser.getOrInsert(id, []).push(line). Review it: what stays the same, what costs more, what can break, and what would you merge instead?
-
Fresher to mid
A cinema booking service records reserved seats with reserved.set([row, seat], bookingId). Every later reserved.has([7, 12]) is false, so seats get booked twice. Explain why, then compare three fixes: a string key, nested Maps and one number per seat.
Key takeaways
- A
Mapstores keys of any type exactly as given:1and'1'are two keys, objects are keys by identity,NaNfinds itself and-0is stored as0. - Keys keep their insertion order; setting an existing key changes the value in place, and deleting and setting it again moves it to the end.
forEachpasses(value, key), whilefor…ofgives[key, value]pairs.JSON.stringifywrites a Map as{}: convert it with[...map]orObject.fromEntries(map)first, and remember thatObject.fromEntriesturns keys into strings.getOrInsert(key, value)returns the stored value or stores the default;getOrInsertComputed(key, fn)callsfnonly for a missing key, so use it when the default costs something to make.- Both methods are ES2026: Node.js 26 and current browsers have them, Node.js 24 does not, and a three-line helper covers the gap.
References
- ECMAScript 2026 Language Specification: Map Objects (Ecma International)
- ECMAScript 2026 Language Specification: Map.prototype.getOrInsert (Ecma International)
- ECMAScript 2026 Language Specification: Map.prototype.getOrInsertComputed (Ecma International)
- ECMAScript 2026 Language Specification: CanonicalizeKeyedCollectionKey (Ecma International)
- ECMAScript 2026 Language Specification: Map.groupBy (Ecma International)
- Proposal: Upsert (getOrInsert and getOrInsertComputed) (Ecma International (TC39))
- TC39 finished proposals (Ecma International (TC39))
- Map (MDN Web Docs)
- Map.prototype.getOrInsert: browser compatibility (MDN Web Docs)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress