JavaScript Maps
A Map is a collection of key-value pairs where keys can be of any type. Unlike plain objects where keys are always strings or symbols, Map keys can be objects, functions, numbers, or any other value.
Creating a Map
You create a Map with the new Map() constructor. You can optionally pass an array of [key, value] pairs to initialize it.
// Empty Map
const map1 = new Map();
console.log("Empty map size:", map1.size);
// Map from array of pairs
const map2 = new Map([
["name", "Alice"],
["age", 25],
["city", "NYC"]
]);
console.log("Map size:", map2.size);
console.log("Name:", map2.get("name"));set(), get(), has(), delete()
set() adds or updates a key-value pair. get() retrieves a value by key. has() checks if a key exists. delete() removes a key-value pair.
const map = new Map();
// set() - returns the Map (chainable)
map.set("name", "Bob");
map.set(42, "forty-two");
map.set(true, "boolean key");
// Any type as key
const objKey = { id: 1 };
map.set(objKey, "object as key");
// get()
console.log("name:", map.get("name"));
console.log("42:", map.get(42));
console.log("true:", map.get(true));
console.log("object:", map.get(objKey));
// has()
console.log("has name:", map.has("name"));
console.log("has age:", map.has("age"));
// delete()
map.delete(42);
console.log("After delete 42, size:", map.size);
// clear()
map.clear();
console.log("After clear, size:", map.size);Iterating Maps
Maps maintain insertion order and provide several ways to iterate: for...of, forEach(), and the keys(), values(), entries() methods.
const user = new Map([
["name", "Alice"],
["age", 25],
["role", "Developer"]
]);
// for...of with destructuring
for (const [key, value] of user) {
console.log(`${key}: ${value}`);
}
// forEach
user.forEach((value, key) => {
console.log(`forEach - ${key}: ${value}`);
});
// keys(), values(), entries()
console.log("Keys:", [...user.keys()]);
console.log("Values:", [...user.values()]);
console.log("Entries:", [...user.entries()]);Map vs Object
Both Maps and Objects store key-value pairs, but they have important differences. Maps are better for frequent additions/removals and when keys are not strings.
| Feature | Map | Object |
|---|---|---|
| Key types | Any type | String or Symbol |
| Size | .size property | Object.keys().length |
| Iteration order | Insertion order | Complex rules |
| Default keys | None | Has prototype keys |
| Performance | Better for frequent add/delete | Better for static data |
| Serialization | Not JSON serializable | JSON.stringify() |
| Syntax | map.get(key) | obj.key or obj[key] |
// Object keys are always strings
const obj = {};
obj[1] = "number";
obj["1"] = "string";
console.log("Object keys:", Object.keys(obj));
console.log("obj[1]:", obj[1]); // 'string' - key coerced!
// Map preserves key types
const map = new Map();
map.set(1, "number");
map.set("1", "string");
console.log("Map size:", map.size);
console.log("map.get(1):", map.get(1));
console.log("map.get('1'):", map.get("1"));
// Map size is a property (fast!)
console.log("Map size:", map.size);
// Object size requires computation
console.log("Object size:", Object.keys(obj).length);Practical Use Cases
Maps are excellent for caching, counting occurrences, storing metadata keyed by DOM elements, and any situation where you need non-string keys.
// Count word occurrences
const text = "the cat sat on the mat the cat";
const words = text.split(" ");
const wordCount = new Map();
for (const word of words) {
wordCount.set(word, (wordCount.get(word) || 0) + 1);
}
wordCount.forEach((count, word) => {
console.log(`"${word}": ${count}`);
});
// Simple cache/memoization
const cache = new Map();
function expensiveCalc(n) {
if (cache.has(n)) return cache.get(n);
const result = n * n;
cache.set(n, result);
return result;
}
console.log("Calc 5:", expensiveCalc(5));
console.log("Calc 5 (cached):", expensiveCalc(5));
console.log("Cache size:", cache.size);