ES6 of the Week

This Week's Episode:

maps and sets

Key : Value

Some ways we store key/value pairs...

  • HashMaps
  • dictionaries
  • hashes

Object downsides

  • Keys must be strings in ES5
  • Challenges with built-in method collisions
  • Objects are not inherently iterable

Sets

  • ordered lists of values which contain no duplicates
  • primitive values AND object references
  • maintain order of insertion
  • can check their equality at a high level

On your mark, get set...

let _set = new Set(); // n.b. set is a reserved word, so I'm calling my variable _set

// Set.prototype.add
_set.add("string").add(42);

// store object references
let obj = { a : 1 };
_set.add(obj);

// Set.prototype.size
_set.size // => 3
_set.add(42) // no repeats
_set.size // => 3

// Set.prototype.has
_set.has("string") // true
_set.has(obj) // true, wowzers!
_set.has(21) // false

// Iterate using for...of
for (let item of _set) console.log(item); // logs in insertion order

// Set.prototype.delete and Set.prototype.clear
_set.delete("string"); // remove a single item
_set.clear(); // wipe the set clean

Arrays

Could have duplicates

 

Actually an object

 

Iterate with for loop

Sets

Unique values

 

No baggage

 

Iterate with for...of

Setting the bar

var bar = ["value1", "value2", "value3"];

// Use the regular Set constructor to transform an Array into a Set
var foo = new Set(bar);

foo.has("value1"); // returns true

// Use the spread operator to transform a set into an Array.
console.log([...foo]); // Will show you exactly the same Array as bar
// but what if bar has duplicate values? 
var bar = ['one fish', 'two fish', 'red fish', 'blue fish', 'blue fish']; 

var foo = new Set(bar);

console.log([...foo])  // <- ['one fish', 'two fish', 'red fish', 'blue fish']

Equality

let persons = new Set();
let randalf = {id: 1, name: 'randalf'};

persons
  .add(randalf)
  .add(randalf);

console.log(`I have ${persons.size} person`)
// => I have 1 person 

console.log([...persons]);
// => [[object Object] {
//  id: 1,
//  name: "randalf"
//}]
persons.add({id: 1, name: 'randalf'});
console.log(`I have ${person.size} persons?!?`)

// => I have 2 persons?!?
console.log([...persons]);
/*
*= [[object Object] {
  id: 1,
  name: "randalf"
}, [object Object] {
  id: 1,
  name: "randalf"
}]
*/

Maps

Are key-value maps

Keys can contain object references, functions, or whatever!

Has handy methods (get the size or iterate)

Follow the Map!

let _map = new Map();

// Map.prototype.set
_map.set('a', 1);
_map.set('b', 2);

let objKey = { 'c' : 3 };
_map.set(objKey, 4);

// Map.prototype.get
_map.get('a'); // => 1
_map.get(objKey); // => 4

// Map.prototype.size - much easier than using Object.hasOwnProperty!
_map.size; // => 3

// Iterate over keys using Map.prototype.keys
for (let key of _map.keys()) console.log(key);

// Iterate over values using Map.prototype.values
for (let value of _map.values()) console.log(value);

Follow the Map!

var map = new Map([
  [new Date(), function today () {}],
  [() => 'key', { full: 'stack' }],
  [Symbol('items'), [1, 2]]
])
var items = [
  [new Date(), function today () {}],
  [() => 'key', { full: 'stack' }],
  [Symbol('items'), [1, 2]]
]

var map = new Map()
items.forEach(([key, value]) => map.set(key, value))
var map = new Map()
map.set('g', 'o')
map.set('t', 'e')
map.set('a', 'm')
for (let [key, value] of map) {
  console.log(`${key}: ${value}`)
}

  // <- 'g: o'
  // <- 't: e'
  // <- 'a: m'

Keys, keys, keys...

var map = new Map()
map.set('a', 'a')
map.set('a', 'b')
map.set('a', 'c')
var map = new Map([[1, 'a']])
console.log(map.has(1))
// <- true
console.log(map.has('1'))
// <- false
console.log([...map])
// <- [['a', 'c']]

Objects

Has a prototype (default key)

 

Keys are strings or symbols

 

Cannot get size easily

Maps

No prototype

 

Keys are any type

 

Can get size easily

WeakSets & WeakMaps

but...why?

WeakSet and WeakMap

  • Have a subset of the methods/properties
    • WS: Add, has, delete
    • WM: Get, set, has, delete
  • objects only!
  • objects are weakly held
    • free up memory!
    • not enumerable (not iterable)

WeakMap example

var obj = getObjectFromLibrary();
function useObj(obj){
   doSomethingWith(obj);
}
var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}
var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

Use Cases

  • Set
    • Any collection of unique values
  • Map
    • Versatile
    • Dictionary-like collections, especially those that will be iterated over
  • WeakSet
    • 'branding' your classes?
  • WeakMap
    • storing metadata for an object
    • prevent memory leaks

ES6 of the Week - 4 (maps & sets)

By beelai88

ES6 of the Week - 4 (maps & sets)

Maps and Sets / Weak maps and weak sets

  • 612