Maps
are data structure composed of a collection of key/value pairs. They are very useful to store simple data, such as property values.
When using Objects as maps its keys
are always converted to strings.
let user1 = { name: "Sam" };
let user2 = { name: "Tyler" };
let totalReplies = {};
totalReplies[user1] = 5;
totalReplies[user2] = 42; // Both objects are converted to the string "[object Object]"
console.log(totalReplies[user1]); // > 42
console.log(totalReplies[user2]); // > 42
console.log(Object.keys(totalReplies)); // > ["[object Object]"]
The Map
object is a simple key/value data structure. Any value may be used as either a key or a value, and objects are not converted to strings.
let user1 = { name: "Sam" };
let user2 = { name: "Tyler" };
let totalReplies = new Map();
totalReplies.set(user1, 5);
totalReplies.set(user2, 42);
console.log(totalReplies.get(user1)); // > 5
console.log(totalReplies.get(user2)); // > 42
We use the get()
and set()
methods to access values in Maps
let recentPosts = new Map();
createPost(newPost, (data) => {
recentPosts.set(data.author, data.message); // Keys unknown until runtime, so... Map!
});
const POSTS_PER_PAGE = 15;
let useSettings = {
perPage: POSTS_PER_PAGE, // Keys are previously defined, so... Object!
showRead: true,
};
let recentPosts = new Map();
createPost(newPost, (data) => {
// All keys are the same type, and all values are the same type, so Map!
recentPosts.set(data.author, data.message);
});
// ...somewhere else in the code
socker.on('new post', function(data) {
// All keys are the same type, and all values are the same type, so Map!
recentPosts.set(data.author, data.message);
});
const POSTS_PER_PAGE = 15;
let useSettings = {
perPage: POSTS_PER_PAGE, // Some values are numeric, others are boolean, so Object!
showRead: true,
};
Maps
are iterable, so they can be used in a for ...of
loop. Each run of the loop returns a [key, value] pair for an entry in the Map
.
let mapSettings = new Map();
mapSettings.set("user", "Sam");
mapSettings.set("topic", "ES2015"];
mapSettings.set("replies", ["Can't wait!", "So Cool"]);
for (let [key, value] of mapSettings) {
console.log(`${key} = ${value}`); // Remember array destructuring?
// > user = Sam
// > topic = ES2015
// > replies = Can't wait!, So Cool
}
The WeakMap
is a type of Map
where only objects can be passed as keys. Primitive data types - such as strings, numbers, booleans, etc. - are not allowed.
let user = {};
let comment = {};
let mapSettings = new WeakMap();
mapSettings.set(user, "user");
mapSettings.set(comment, "comment");
console.log(mapSettings.get(user)); // > user
console.log(mapSettings.get(comment)); // > comment
// Primitive data types are not allowed
mapSettings.get("title", "ES2015"); // > Invalid value used as weak map key
All available methods on a WeakMap
require access to an object used as a key.
let user = {};
let mapSettings = new WeakMap();
mapSettings.set(user, "ES2015");
console.log(mapSettings.get(user)); // > ES2015
console.log(mapSettings.has(user)); // > true
console.log(mapSettings.delete(user)); // > true
WeakMaps
are not iterable, therefore they can't be used with for ...of
for (let [key, value] of mapSettings) {
console.log(`${key} = ${value}`); // > mapSettings[Symbol.iterator] is not a function
}
Individual entries in a WeakMap
can be garbage collected while the WeakMap
itself still exists.
let user = {}; // All objects occupy memory space
let userStatus = new WeakMap();
userStatus.set(user, "logged"); // Object reference (weak) passed as key to the `WeakMap`
// ...
someOtherFunction(user); // Once it returns, `user` can be garbage collected
WeakMaps
don't prevent the garbage collector from collecting objects currently used as keys, but that are no longer referenced anywhere else in the system