Keywords: ES6 Map | Filtering Operations | Iterator Safety
Abstract: This article explores filtering operations for ES6 Maps, analyzing two primary approaches: immutable filtering by creating a new Map and mutable filtering via in-place deletion. It focuses on the safety of deleting elements during iteration, explaining the behavioral differences between for-of loops and keys() iterators based on ECMAScript specifications. Through performance comparisons and code examples, best practices are provided, including optimizing key-based filtering with the keys() method and discussing the applicability of Map.forEach. Alternative methods via array conversion are also covered to help developers choose appropriate strategies based on their needs.
Core Mechanisms of ES6 Map Filtering
ES6 Maps, as key-value pair collections in JavaScript, offer efficient storage and retrieval but lack a built-in .filter() method similar to arrays. Developers often need to implement custom filtering logic, leading to discussions on safety and performance.
Immutable Filtering: Creating a New Map
A common approach is to create a new Map to store filtered results, ensuring the original data remains unmodified. Example code:
function filterMap(map, predicate) {
const filteredMap = new Map();
for (const [key, value] of map) {
if (predicate(key, value)) {
filteredMap.set(key, value);
}
}
return filteredMap;
}
const originalMap = new Map([
[1, "one"],
[2, "two"],
[3, "three"]
]);
const filtered = filterMap(originalMap, (k, v) => k % 2 === 0);
console.log([...filtered]); // Output: [[2, "two"]]
This method avoids modifying the original Map, suitable for scenarios requiring data immutability, but may incur memory overhead.
Mutable Filtering: In-Place Deletion
To modify the original Map directly, elements that do not meet criteria can be deleted during iteration. A key concern is whether deletion affects iterator stability. According to the ECMAScript specification, Map iterators remain functional after deleting the current element, but implementation details should be noted.
function deleteIfNot(map, predicate) {
for (const [key, value] of map) {
if (!predicate(key, value)) {
map.delete(key);
}
}
return map;
}
const map = new Map([
[1, "one"],
[2, "two"],
[3, "three"]
]);
deleteIfNot(map, (k, v) => k % 2 === 0);
console.log([...map]); // Output: [[2, "two"]]
This method is correct on all ES6-compliant platforms, as the specification defines iterator behavior to continue valid after element deletion.
Performance Optimization: Using the keys() Method
When filtering depends only on keys, using Map.prototype.keys() can improve efficiency by avoiding unnecessary value access. Example:
for (const key of map.keys()) {
if (!(key % 2 === 0)) {
map.delete(key);
}
}
This approach reduces iteration overhead, especially beneficial for key-based filtering of large Maps.
Alternative Approach: Array Conversion Method
Referencing other answers, filtering can be achieved via array conversion, leveraging array's .filter() method:
const map0 = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
const map1 = new Map(
[...map0].filter(([k, v]) => v < 3)
);
console.log([...map1]); // Output: [['a', 1], ['b', 2]]
This method offers concise code but involves array creation and conversion, potentially impacting performance, making it suitable for small datasets or prototyping.
Applicability Analysis of Map.forEach
The Map.prototype.forEach method can also be used for filtering, but it is designed primarily for value processing rather than key-value pair operations. In filtering contexts, it may be less flexible than direct iteration, as the callback function mainly receives value parameters, requiring additional handling for keys.
Conclusion and Best Practices
Filtering ES6 Maps requires strategy selection based on needs:
- Use in-place deletion (via
for-oforkeys()) for high-performance mutable filtering. - Adopt new Map creation to ensure data immutability.
- Leverage array conversion for code simplicity, but consider performance trade-offs.
Understanding iterator behavior post-deletion is crucial for code correctness, while optimizing key access can further enhance efficiency.