Map get and delete S1
- Stage: Stage 1
- Status: Active
- ECMAScript edition: —
- Synchronized: Aug 28, 2026
- 中文译文 · Source repository
The proposal adds getAndDelete(key) to Map and WeakMap, combining a value read and entry removal into a single operation. It returns the removed value (or undefined if the key is absent), replacing the current two-step get + delete pattern that requires two lookups and is easy to get wrong. The proposal also discusses how to handle the ambiguity between a missing key and a present undefined value, considering direct return, an optional fallback value, or a tagged result.
The README below comes from the upstream repository and may contain outdated stage or status metadata. Use the proposal details above as the current source of truth.
Map.prototype.getAndDelete & WeakMap.prototype.getAndDelete
A proposal to add a getAndDelete(key) method to Map and WeakMap that removes the entry for key and returns its value (or undefined if the key was absent) in a single operation.
- Stage: 1
- Champion: Devin Rousso
- Authors: Devin Rousso
Motivation
"Read a value and remove it in the same step" is one of the most common things people do with a map (e.g. consume a pending callback keyed by request id, pop a job off a work queue, evict and inspect a cache entry, hand off ownership of a buffered chunk, drain a "waiting for X" table, etc.).
JavaScript can express every other member of the CRUD quartet as a single call that returns something useful (e.g. map.get(k), map.set(k, v) (returns the map), and map.has(k)) except the "remove and read" combination.
Map.prototype.delete returns a boolean telling you whether anything was removed (i.e. it throws the value away).
So today you must write the value-preserving version by hand, which requires two lookups of the same key:
This has real downsides:
- Two lookups instead of one. For hot paths (dispatch tables, per-frame caches, etc.) the extra lookup is pure overhead an engine could avoid if the operation were a single method.
- It's a footgun to inline.
const v = map.get(k); map.delete(k); return v;is easy to reorder incorrectly (i.e.deletebefore capturing the value) and thedeleteboolean result is silently discarded so linters can't help. - Everyone re-implements it. The helper is reinvented under a dozen names (e,g,
getAndDelete,getAndRemove,pop,pull,popEntry,take,fetch, etc.) in codebase after codebase.
Proposal
getAndDelete(key) is defined to be observably equivalent to reading get(key) and then performing delete(key), returning the value that get would have returned.
It mutates the map in place and returns the value directly (i.e. it does not return the map) because the whole point is to hand the value back to the caller.
Examples
Semantics
Like get, a return of undefined is ambiguous between "absent" and "present with value undefined".
Unlike get, callers cannot resolve that ambiguity by calling has(key) after receiving undefined, because getAndDelete has already removed a matching entry.
Calling has(key) beforehand works, but requires a second lookup.
Whether getAndDelete should provide a way to distinguish these cases with one lookup remains an open question below.
Distinguishing a Missing Key from an undefined Value
A Map can store any ECMAScript language value, including any Symbol or object that an API could expose as a sentinel for a missing value.
If getAndDelete must return every present value unchanged, no sentinel provided by the language can avoid every collision because that sentinel could itself be stored in the map.
The same problem came up in the getIntrinsic proposal. Any sentinel could also be a valid result, but using a container would have worse ergonomics.
The existing form with two calls can preserve both the removed value and whether the key existed:
Returning the value directly would reduce this to one lookup only when the caller does not need found.
As a result, there are three options that seem likely to be considered by TC39.
1. Keep returning the value directly
The proposal could keep the current behavior and require callers that need the distinction to call has first:
This would keep the API small, avoid creating a result object, and continue to return the removed value directly.
It would also follow existing JavaScript APIs.
Array.prototype.pop has the same ambiguity because an empty array and an array whose last value is undefined both produce undefined.
Map.prototype.get and Array.prototype.find have similar behavior.
An earlier getOrInsert discussion also argued that undefined was acceptable because JavaScript does not have an Option or Maybe type and Map.prototype.get already behaves this way.
The downside is that callers that need the distinction would still perform two lookups.
The Array.prototype.pop precedent does not have the same performance cost because checking array.length does not repeat an element lookup, while calling map.has(key) repeats the lookup for key.
This is probably the option most likely to be accepted because it matches the direct precedent and does not add complexity for every caller.
2. Optional fallback value
getAndDelete could accept a second value that is returned only when the key is absent:
Calling getAndDelete(key) with one argument would continue to return undefined when the key is absent.
This would preserve the direct return value, require one lookup, and avoid creating a result object.
A private Symbol could be created once and reused, while an ordinary value could also be used as a default.
The main issue is that the caller must reserve a value that the map will not contain in order to guarantee the distinction.
The default value would also be evaluated before the method runs, even when the key is present.
This is probably the most plausible addition if the committee wants a way to distinguish the cases with one lookup.
It follows Python's dict.pop and the default value form of the Stage 4 Map.prototype.getOrInsert.
The earlier Map.prototype.getWithDefault discussion also recognized that map.get(key) ?? fallback is not equivalent because a present value of undefined must not use the fallback.
Map.prototype.getOrInsert now makes this same distinction internally, returning a present value of undefined and using the provided value only when the key is absent.
3. Always return a tagged result
The method could instead return an object containing both pieces of information:
For example, the result could always have the shape { found, value }, where value is undefined when found is false.
This would always distinguish the cases, preserve every stored value, and require one lookup.
Tagged results are also already used by iterator APIs and Promise.allSettled, so the shape would not be new to JavaScript.
A related version could use the wrapper itself to indicate that the key existed:
This version would return { value } when the key existed and undefined otherwise. It would avoid creating an object when the key is absent, but it would still wrap every removed value.
Object.getOwnPropertyDescriptor similarly returns an object when the property exists and undefined otherwise. TC39 also discussed this exact approach for Array.prototype.find, where a successful result could wrap any value, including undefined, while a missing result would remain undefined.
JavaScript does not have the Box or Option type discussed there, so getAndDelete would need to create its own result object.
The downside is that every call would need to unpack a result instead of using the removed value directly.
It would also create a result object for every call.
The getOrInsert discussion raised similar concerns about allocations in frequently called map code, and its final design removed the options object.
That discussion concerned input objects rather than a returned result object, so it does not decide this question. It does show that allocations in frequently called map operations are likely to receive scrutiny.
Since avoiding overhead is one of the main motivations for getAndDelete, this would be a significant cost.
Earlier versions of getOrInsert also tried to handle insertion and update callbacks in a single API.
Review feedback was that the API was unintuitive because it tried to do too many things, and the Stage 4 design now focuses on the common operation instead.
Based on that history, the proposal should focus on these three choices instead of trying to cover every possible way to return the status.
Prior Art
"Remove and return the value" is the default shape of this operation in most languages' standard libraries.
JavaScript is the outlier in returning only a boolean from delete.
Precedents
getAndDelete is not a foreign shape for JavaScript as the language already has a "remove an element and return its value" method in Array.prototype.pop.
It mutates the collection in place, returns the removed element, and yields undefined when there is nothing to remove.
getAndDelete applies that exact contract to keyed collections (i.e. addressed by key instead of by position):
Because pop already establishes "mutate and hand back the value" as an idiomatic JavaScript pattern, getAndDelete is a small, consistent extension of it rather than a new concept (i.e. it just removes by key).
Naming
getAndDeletenames the two existing operations it combines and extends the family ofget,getOrInsert,getOrInsertComputed, etc. The order of the read and mutation is explicit and greppable.takematches Rust'sHashSet::take("remove and return") and reads naturally "take the value out of the map".popis misleading asArray.prototype.poptakes no argument and removes from the end (i.e. LIFO) instead of by index.removewould sit confusingly beside the existingdelete(i.e. two spellings with different return types) and collides with the many userlandremovemethods.
The proposal adopted getAndDelete when advancing to Stage 1.
Usage
Counts below come from GitHub
GET /search/codewhich is a token-based index, covers only the default branch of public repos, and does not verify that the matched tokens act on the same key/variable.
Examples
Questions
- Is the ambiguity already present in
Map.prototype.getandArray.prototype.findacceptable forgetAndDelete? - If not, should
getAndDeleteaccept a fallback value or always return a tagged result? - This sketch returns
undefined, mirroringget, sogetAndDeleteis a drop-in replacement for thegetanddeletepair, but an alternative could be a Python-style optional defaultgetAndDelete(key, defaultValue). - Is it worth adding
Set.prototype.takeand/orWeakSet.prototype.takefor parity like howSet.prototype.entriesreturns a two-value array to matchMap.prototype.entries?