Making a Data Structure Iterable in JavaScript & TypeScript

In my last post I explained how to make a Map and Set that allowed you to use coordinates ([number, number] arrays) more like values than references. This means that adding a coordinate array with the same values twice will only result in one entry in the respective data structures. For example, in the CoordinateSet data structure you can see this behaviour where we add the same coordinate twice but the size remains at 0:

coordinateSet.add([51.509865, -0.118092]);
coordinateSet.add([51.509865, -0.118092]);
coordinateSet.size; // 1

Here I realised I wanted to extend CoordinateSet to be closer to the Set class. This meant adding the entries and forEach methods and also handling spreadable and for...of behaviour.

This got me looking at how these work for Set. Turns out set implements Set.prototype[@@iterator](), which allows it to be iterated over. An object is considered iterable if the object implements the iterable protocol - some examples include Arrays, Maps and Sets.

The @@iterator property is accessible via Symbol.iterator, and must return an object that conforms to the iterator protocol. Luckily, using a generator function we can ensure that an iterator object is returned. Generator functions are pretty cool and can be defined by adding an asterix onto the end of a function keyword, i.e. function*. In this specific case we use a computed property, which means we are defining it using a value, in this case Symbol.iterator. This syntax is slightly different as we have to put the asterix before *[Symbol.iterator]().

Let's assume we have a Class (in our case CoordinateSet from the previous post), here's how we'd do it:


// This bit allows the data structure to be iterated over with
// spread syntax ([...]) and for...of
*[Symbol.iterator]() {
for (let entry of this.map.entries()) {
yield [entry[1], entry[1]];
}
}

// Matching the Set.entries interface
entries() {
return this[Symbol.iterator]();
}

Let's break down the bits that might be a bit alien here:

There's quite a bit to take in there if you're not familiar with generators and Symbols. I would recommend taking a bit of a look at the MDN documentation for these two and the above code will start to make a little more sense!

Published