Notes Javascript - Keyed Collections
Keyed Collections
Keyed collections differ from Arrays in how they index information. An array uses integers to reference an item. A keyed collection uses a key, which can take many forms. Keyed collections in Javascript include the Map Object and the Set Object.
Map Object
A Map object is a simple key/value map and can iterate its elements in insertion order.
const sayings = new Map(); sayings.set("dog", "woof"); sayings.set("cat", "meow"); sayings.set("elephant", "toot"); sayings.size; // 3 sayings.get("dog"); // woof sayings.get("fox"); // undefined sayings.has("bird"); // false sayings.delete("dog"); sayings.has("dog"); // false for (const [key, value] of sayings) { console.log(`${key} goes ${value}`); } // "cat goes meow" // "elephant goes toot" sayings.clear(); sayings.size; // 0
Object And Map Compared
Traditionally, objects have been used to map strings to values. Objects allow you to set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Map objects, however, have a few more advantages that make them better maps.
- The keys of an Object are Strings or Symbols, where they can be of any value for a Map.
- You can get the size of a Map easily, while you have to manually keep track of size for an Object.
- The iteration of maps is in insertion order of the elements.
- An Object has a prototype, so there are default keys in the map. (This can be bypassed using map = Object.create(null).)
These three tips can help you to decide whether to use a Map or an Object:
- Use maps over objects when keys are unknown until run time, and when all keys are the same type and all values are the same type.
- Use maps if there is a need to store primitive values as keys because object treats each key as a string whether it's a number value, boolean value or any other primitive value.
- Use objects when there is logic that operates on individual elements.
WeakMap Object
There is another Map-like object called a WeakMap Object. The following is a brief description of what a WeakMap is. There won't be any discussion of it beyond the description.
A WeakMap is a collection of key/value pairs whose keys must be objects or non-registered symbols, with values of any arbitrary JavaScript type, and which does not create strong references to its keys. That is, an object's presence as a key in a WeakMap does not prevent the object from being garbage collected. Once an object used as a key has been collected, its corresponding values in any WeakMap become candidates for garbage collection as well, as long as they aren't strongly referred to elsewhere. The only primitive type that can be used as a WeakMap key is symbol, more specifically, non-registered symbols, because non-registered symbols are guaranteed to be unique and cannot be re-created.
The WeakMap API is essentially the same as the Map API. However, a WeakMap doesn't allow observing the liveness of its keys, which is why it doesn't allow enumeration. So there is no method to obtain a list of the keys in a WeakMap. If there were, the list would depend on the state of garbage collection, introducing non-determinism.
One use case of WeakMap objects is to store private data for an object, or to hide implementation details.
Set Object
Set objects are collections of unique values. You can iterate its elements in insertion order. A value in a Set may only occur once; it is unique in the Set's collection.
The following code shows some basic operations with a Set.
const mySet = new Set(); mySet.add(1); mySet.add("some text"); mySet.add("foo"); mySet.has(1); // true mySet.delete("foo"); mySet.size; // 2 for (const item of mySet) { console.log(item); } // 1 // "some text"
Array And Set Compared
Traditionally, a set of elements has been stored in arrays, the Set object, however, has some advantages:
- Deleting Array elements by value (arr.splice(arr.indexOf(val), 1)) is very slow.
- Set objects let you delete elements by their value. With an array, you would have to splice based on an element's index.
- The value NaN cannot be found with indexOf in an array.
- Set objects store unique values. You don't have to manually keep track of duplicates.
Converting Between Array And Set
You can create an Array from a Set using Array.from or the spread syntax. Also, the Set constructor accepts an Array to convert in the other direction.
Set objects store unique values—so any duplicate elements from an Array are deleted when converting from Array to Set!
const myArr = ["Some text 1","Some text 2","Some text 3"] const mySet = new Set(); mySet.add("Some text 1"); mySet.add("Some text 2"); mySet.add("Some text 3"); // SET TO ARRAY var myAr1 = Array.from(mySet); var myAr2 = [...mySet]; // ARRAY TO SET mySet2 = new Set([1, 2, 3, 4]); mySet3 = new Set(myArr);
function runScript1() { let divResult = document.getElementById("results1"); let myArr = ["Array text 1","Array text 2","Array text 3"] let mySet = new Set(); mySet.add("Set text 1"); mySet.add("Set text 2"); mySet.add("Set text 3"); // CREATE AN ARRAY FROM mySet USING Array.from() let myArr2 = Array.from(mySet); // CREATE AN ARRAY FROM mySet USING SPREAD NOTATION let myArr3 = [...mySet]; // CREATE A SET FROM AN ARRAY let mySet2 = new Set(myArr); // SHOW CONTENTS OF ARRAY 1 divResult.innerHTML = "Array 1:<br/>"; for (var i = 0; i < myArr2.length; i++) { divResult.innerHTML += myArr2[i] + "<br/>"; } // SHOW CONTENTS OF ARRAY 2 divResult.innerHTML += "Array 2:<br/>"; for (var i = 0; i < myArr3.length; i++) { divResult.innerHTML += myArr3[i] + "<br/>"; } // SHOW CONTENTS OF SET 1 divResult.innerHTML += "Set 1:<br/>"; for (const item of mySet2) { divResult.innerHTML += item + "<br/>"; } }
Run Test Script