· 6 min read
Map getOrInsert and getOrInsertComputed: upsert in JavaScript
Map.getOrInsert() and getOrInsertComputed() let you read a key or insert a default in one call. How they differ, edge cases and support.

Map.prototype.getOrInsert() and getOrInsertComputed() are new built-in JavaScript methods that return the value for a key, and insert a default first if the key is missing. They replace the if (!map.has(key)) map.set(key, ...) dance with one call that does a single lookup. Both methods, plus matching WeakMap versions, became Baseline Newly available in February 2026.
If you have ever grouped items into a Map of arrays, counted words, or cached results by key, you have written the long version of this many times.
What problem do getOrInsert and getOrInsertComputed solve?
The pattern is called upsert: update the entry if it exists, insert it if it does not. It is the name of the TC39 proposal behind these methods. In plain JavaScript it usually looks like this:
const groups = new Map();
for (const word of words) {
const key = word[0];
if (!groups.has(key)) {
groups.set(key, []);
}
groups.get(key).push(word);
}
That works, but it is three operations on the map for one logical step: has, then set, then get. It is also easy to get subtly wrong. A common shortcut is map.get(key) || [], which breaks for falsy values like 0 or "", and forgets to store the new array, so every push lands in a throwaway object.
With the new method, the loop body becomes one line:
const groups = new Map();
for (const word of ["apple", "avocado", "banana"]) {
groups.getOrInsertComputed(word[0], () => []).push(word);
}
// Map { "a" => ["apple", "avocado"], "b" => ["banana"] }
The method either returns the array already stored under that key, or creates one, stores it, and returns it. Either way you get back the live value in the map, so mutating it works.
How do the two methods differ?
There are two methods because there are two kinds of defaults: cheap ones and expensive ones.
getOrInsert(key, value)takes the default value directly. If the key exists, it returns the existing value and ignores the one you passed. If not, it stores your value and returns it.getOrInsertComputed(key, callback)takes a function. The callback runs only when the key is missing, receives the key as its only argument, and its return value is stored and returned.
The difference matters because JavaScript evaluates arguments before calling a function. With getOrInsert, the default is always built, even when it is thrown away:
const m = new Map([["k", 1]]);
let built = 0;
m.getOrInsert("k", (built++, []));
console.log(built); // 1 — the array was created and discarded
let called = 0;
m.getOrInsertComputed("k", () => { called++; return []; });
console.log(called); // 0 — the callback never ran
So the rule of thumb is simple. Use getOrInsert for primitives and constants like 0, "" or null. Use getOrInsertComputed for anything that allocates, does work, or has side effects.
Counting is the classic case for getOrInsert:
const counts = new Map();
for (const w of ["a", "b", "a"]) {
counts.set(w, counts.getOrInsert(w, 0) + 1);
}
// Map { "a" => 2, "b" => 1 }
You still call set here, because numbers are immutable and you are replacing the value, not mutating it. The method only removes the "is it there yet?" branch.
How does getOrInsertComputed handle edge cases?
A few details are worth knowing before you lean on it.
The callback must be callable. Passing anything other than a function throws a TypeError, even if the key already exists.
Keys follow normal Map rules. Objects are compared by reference, and -0 is treated the same as 0, just like set and get.
The callback's result wins. The specification looks up the key, calls your callback if it is missing, and then looks again before writing. If the callback itself inserted that same key, the callback's return value overwrites it:
const m = new Map();
const result = m.getOrInsertComputed("k", () => {
m.set("k", "inner");
return "outer";
});
console.log(result, m.get("k")); // "outer" "outer"
You should not write code that relies on this, but it means the method never returns a value that differs from what ends up in the map.
WeakMap needs weak-holdable keys. WeakMap.prototype.getOrInsert and getOrInsertComputed behave the same way, but the key must be something a WeakMap can hold, such as an object. A string key throws a TypeError, as it does with WeakMap.prototype.set.
const meta = new WeakMap();
const node = {};
meta.getOrInsertComputed(node, () => ({ visits: 0 })).visits++;
This is a tidy way to attach per-object metadata without touching the object itself.
When should you use getOrInsertComputed for caching?
Memoization is where the computed version is most useful. Memoization means storing the result of a call so the next call with the same input returns the stored result instead of redoing the work.
const cache = new Map();
function loadUser(id) {
return cache.getOrInsertComputed(id, (id) =>
fetch(`/api/users/${id}`).then((r) => r.json())
);
}
Notice that the map stores the promise, not the resolved data. If ten callers ask for the same user at once, the first call inserts the promise and the other nine get that same promise back. Only one request goes out. That is the same request-coalescing idea used to stop a cache stampede, applied inside a single process.
Two cautions. A rejected promise stays in the map, so you usually want to delete the entry on failure. And a plain Map cache never evicts anything, so give it a size limit or a lifetime if the set of keys can grow without bound.
Which browsers and runtimes support it?
According to the web-features data, support landed in this order:
| Engine | First version |
|---|---|
| Firefox | 144 |
| Safari | 26.2 |
| Chrome and Edge | 145 |
The Edge 145 release in February 2026 is what made the feature Baseline Newly available. It is expected to become Baseline Widely available around August 2028.
On the server, support depends on the V8 version your runtime bundles. Node.js 22, for example, does not have these methods. Feature-detect before you rely on them:
if (typeof Map.prototype.getOrInsertComputed !== "function") {
// load a polyfill, or fall back to has/set/get
}
Polyfill libraries such as core-js include implementations if you need to support older environments.
Key takeaways
getOrInsertandgetOrInsertComputedreturn a key's value and insert a default first if it is missing.- Use
getOrInsertfor cheap defaults like0; usegetOrInsertComputedwhen the default allocates or does work, because the callback only runs on a miss. - Both methods return the live value stored in the map, so grouping into arrays becomes one line.
- Storing promises with
getOrInsertComputedgives you simple in-process request deduplication. - The feature is Baseline Newly available since February 2026; feature-detect it in Node.js and older browsers.
FAQ
Is getOrInsert the same as Python's setdefault?
Very close. Python's dict.setdefault(key, default) behaves like getOrInsert, including evaluating the default eagerly. getOrInsertComputed is closer to defaultdict, where a factory builds the default only when needed.
Does getOrInsertComputed work with async callbacks?
The callback can return anything, including a promise, and that promise is what gets stored. The method itself does not await it, which is exactly why it works well for deduplicating concurrent requests.
Do plain objects get these methods?
No. They exist only on Map and WeakMap. For plain objects, the ??= operator gives a similar result: obj[key] ??= [].