Skip to Content
JS NativeMap

JavaScript Maps, WeakMaps, and Map.groupBy()

Explore how Map, WeakMap, and Map.groupBy() work in JavaScript. Learn the differences between Maps and plain objects, how WeakMap handles memory safely, and how to group values using custom groupBy().

Map

The Map object holds key-value pairs and remembers the original insertion order of the keys. Keys can be any value, including objects, functions, and NaN. Unlike objects, maps offer consistent key ordering and better performance for frequent additions and deletions.

Instance methods:

  • Map.prototype.clear()
  • Map.prototype.delete()
  • Map.prototype.entries()
  • Map.prototype.forEach()
  • Map.prototype.get()
  • Map.prototype.has()
  • Map.prototype.keys()
  • Map.prototype.set()
  • Map.prototype.values()

Instance properties:

  • Map.prototype.size
Map
const map = new Map(); map.set("name", "Alice"); map.set("age", 25); map.set("country", "USA"); console.log(map); // Map(3) { 'name' => 'Alice', 'age' => 25, 'country' => 'USA' } // Size console.log(map.size); // 3 // Accessing values console.log(map.get("name")); // Alice // Checking for a key console.log(map.has("age")); // true // Removing a key map.delete("country"); console.log(map); // Map(2) { 'name' => 'Alice', 'age' => 25 } map.set("city", "New York"); map.set("hobby", "Painting"); // Using for...of console.log("Using for...of:"); for (const [key, value] of map) { console.log(key, ":", value); } // Getting all keys, values, or entries console.log("Keys:", [...map.keys()]); // Keys: [ 'name', 'age', 'city', 'hobby' ] console.log("Values:", [...map.values()]); // Values: [ 'Alice', 25, 'New York', 'Painting' ] console.log("Entries:", [...map.entries()]); // Entries: [ // [ 'name', 'Alice' ], // [ 'age', 25 ], // [ 'city', 'New York' ], // [ 'hobby', 'Painting' ] // ] // Clearing the map map.clear(); console.log(map); // Map(0) {} // Creating a Map with an array of entries const mapFromArray = new Map([ ["fruit", "Apple"], ["color", "Red"], ["quantity", 10], ]); console.log(mapFromArray); // Map(3) { 'fruit' => 'Apple', 'color' => 'Red', 'quantity' => 10 } // Converting a Map back to an object const obj = Object.fromEntries(mapFromArray); console.log(obj); // { fruit: 'Apple', color: 'Red', quantity: 10 } // Map vs Object comparison const objExample = { a: 1, b: 2, c: 3 }; const mapExample = new Map(Object.entries(objExample)); console.log(mapExample); // Map(3) { 'a' => 1, 'b' => 2, 'c' => 3 } // Keys of different types const map2 = new Map(); const keyObj = {}; const keyFunc = function () {}; map2.set("hello", "string value"); map2.set(keyObj, "obj value"); map2.set(keyFunc, "func value"); map2.set(NaN, "NaN value"); console.log(map2); // Map(4) { // 'hello' => 'string value', // {} => 'obj value', // [Function: keyFunc] => 'func value', // NaN => 'NaN value' // } console.log(map2.get("hello")); // string value console.log(map2.get(keyObj)); // obj value console.log(map2.get(keyFunc)); // func value console.log(map2.get(NaN)); // NaN value

WeakMap

The WeakMap object is a special kind of map whose keys must be objects, and those keys are weakly referenced — meaning they do not prevent garbage collection.

This makes WeakMap useful for storing metadata or private data tied to object lifetimes. However, it does not support iteration or methods like .keys() or .clear().

  • weakMap.set(key, value)
  • weakMap.get(key)
  • weakMap.delete(key)
  • weakMap.has(key)

If an object has lost all other references, then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens

WeakMap
let john = { name: "John" }; const map = new Map(); map.set(john, "..."); john = null; console.log(map.get(john)); // undefined console.log(map.has(john)); // false console.log(map.keys()); // [Map Iterator] { { name: 'John' } } let doe = { name: "Doe" }; const weakMap = new WeakMap(); weakMap.set(doe, "..."); doe = null; console.log(weakMap.get(doe)); // undefined // weakMap.keys() not supported

Map.groupBy

The Map.groupBy() static method groups elements from an iterable based on the return value of a callback function. It returns a new Map where each key is the grouping criterion and each value is an array of items in that group.

This example provides a polyfill-style custom implementation of Map.groupBy() and demonstrates grouping numbers, objects by property, and even using objects as keys for advanced use cases.

Map.groupBy
// Map.groupBy isn't available yet function groupBy(array, callback) { const map = new Map(); for (const item of array) { const key = callback(item); const group = map.get(key) || []; group.push(item); map.set(key, group); } return map; } // Using Map.groupBy to group numbers by even and odd const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; const groupedNumbers = groupBy(numbers, (num) => num % 2 === 0 ? "even" : "odd", ); console.log(groupedNumbers); // Map(2) { // 'odd' => [1, 3, 5, 7, 9], // 'even' => [2, 4, 6, 8] // } // Grouping people by their age group const people = [ { name: "Alice", age: 25 }, { name: "Bob", age: 30 }, { name: "Charlie", age: 25 }, { name: "David", age: 35 }, ]; const groupedByAge = groupBy(people, (person) => person.age); console.log(groupedByAge); // Map(3) { // 25 => [ { name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 } ], // 30 => [ { name: 'Bob', age: 30 } ], // 35 => [ { name: 'David', age: 35 } ] // } const inventory = [ { name: "asparagus", type: "vegetables", quantity: 9 }, { name: "bananas", type: "fruit", quantity: 5 }, { name: "goat", type: "meat", quantity: 23 }, { name: "cherries", type: "fruit", quantity: 12 }, { name: "fish", type: "meat", quantity: 22 }, ]; // Using objects as keys const restock = { restock: true }; const sufficient = { restock: false }; const result = groupBy(inventory, ({ quantity }) => quantity < 6 ? restock : sufficient, ); console.log(result); // Map(2) { // { restock: false } => [ // { name: 'asparagus', type: 'vegetables', quantity: 9 }, // { name: 'goat', type: 'meat', quantity: 23 }, // { name: 'cherries', type: 'fruit', quantity: 12 }, // { name: 'fish', type: 'meat', quantity: 22 } // ], // { restock: true } => [ { name: 'bananas', type: 'fruit', quantity: 5 } ] // } console.log(result.get(restock)); // [{ name: "bananas", type: "fruit", quantity: 5 }]
Last updated on