Get In With the In-Set: JavaScript Sets

March 12, 2018 0 Comments

Get In With the In-Set: JavaScript Sets



One of the great things about JavaScript is that it is an incredibly forgiving language — it bends over backwards to never give you an error message if there’s any possible way to avoid it, coerces type if you so much as blink at it, and lets you treat arrays with the elasticity of a Grow Monster.

This can feel great and safe when you’re a beginning developer — fewer errors and fewer rules must be a good thing, right?

That sound you hear is the uproarious, embittered laughter of all the older devs reading this over your shoulder.

Errors and restrictions, in fact, mostly overall help us create better code. Ideally, if something is going wrong, we want to know about it, and if being able to do something is going to screw us up later on, it might just be better if we’re not allowed to do it in the first place. In that way, while JavaScript is often touted as a beginner-friendly language, it has a number of deceptively innocuous pitfalls. Think of it kind of like the difference between using free weights and weight machines at the gym — the former give you more freedom, but that’s also more freedom to make a mess of yourself. The good news is: the good people of Ecma International are constantly coming up with new ways to save you from your own bad choices. And JavaScript sets are a part of that.

So, what is a set? A set is kind of like an array, and kind of like an object, and kind of its own thing altogether. Basically, a set is that really strict, judge-y teacher you had in high school whom you loathe while you’re in her class, but then realize years later in a touching black and white montage that she taught you all sorts of Important Life Lessons about discipline and clear thinking.

More practically: a set is a complex data-type which stores other pieces of data (or references to them — more on that later), which can be of any type (even other sets!). The most important thing to remember about sets is that they are always a collection of unique values — a set, unlike an array or an object, will only ever store a given value once.

How can I use sets? (The Fun Part)

Making a set: Set is a JS class, so you can instantiate a set using the ‘new’ keyword, like this:

 let thingy = new Set(); //this creates an empty set

This will give you a squeaky-clean, totally empty set. You can also pass any “iterable” to the set and have its elements added (excluding any repeats). An iterable sounds like it should be a fairly intuitive category, but let’s talk about it anyhow: an array is one, a set is one. Those are nice and straightforward! A string is an iterable in this case too — if you pass a string to the set constructor, you will get a set with each unique character as an element in the set. So that means these sets:

const code = new Set(‘code’);
const moreCode = new Set(‘ccooddee’); //repeats excluded
const extraCode = new Set(['c','o','d','e']);
const codeClone = new Set(code); //referencing our first set, of which this will be a clone

will all result in a set that looks like this:

Set { ‘c’, ‘o’, ‘d’, ‘e’ }

Neither numbers nor objects can be used in this way, although they can both be added to already established sets. (We can add numbers or objects to a set when we instantiate it — by putting them in an array or another set).

Once you have a set, you can add to it using the add method, which will take any data-type as its value. What’s really cool is that the add method actually returns the set itself, so that you can do THIS:

const mySet = new Set([‘you’]);
// prints: Set { ‘you’, ‘can’, ‘chain’, ‘sets’, ‘wow’, ‘!’);

Chaining, guys. It’s rad.

Because a set is not an array, even though it is a list, it doesn’t have a .length property — it has a .size one instead, which will return the number of elements in a set.

Already, we can start to see how this could solve some problems very neatly and efficiently, right? Say we had a dozen mailing lists that we had bought, and wanted to combine them, so that we had one mega list. We’d want to be sure that there were no duplicates in our master list, so that we didn’t spam anyone, right? And, surely, we’d want to know how many real, non-duped people we actually had altogether?

const mailingList = new Set(list1).add(list2).add(list3) //etc.
const sumOfList = mailingList.size;

And just like that, we have our perfect list, and we know exactly how long it is. And, if we ever need to remove someone from our list, we don’t need to go looking for them — we don’t even have to be 100% sure that they were ever in our list to begin with. We can just:

const byeBye = mailingList.delete(personLeaving);

and the set will be guaranteed to not have that value in it anymore. And, if we took a peek at byeBye, we’d find that we had a boolean, reflecting whether or not that value had in fact needed to be deleted in the first place.

What we don’t have, for better or worse, is random access. Which is a little funny, because sets do possess some order, like but unlike in an array. We can use set.values() to give us an iterator object that has all the values in a set in their order of insertion (thus, this order cannot be manipulated, except by removing and re-inserting elements). Despite this, we have no way to say either set[0] or set.someProperty. We can check for the presence of a value using the set.has(val) method, which will return a boolean. But we can’t use that to grab or otherwise alter that particular piece of data.

If what we want is to systematically access every value in a set, we will need a set.forEach() — again, both like and unlike our trusty, familiar array method. Set’s forEach() takes a function as a parameter, and will execute once on each value in the set. We technically can do some manipulation on the Set itself this way, some of the things we might do to mutate an array with array.forEach() just won’t wash. (If you use .add in the same way you could use .push in an array forEach(), you will quickly find yourself with a fatal error, and your forEach() valiantly trying to reach the end of a set that forever lengthens in front of it.) Certainly, if we have objects in our set, we can add properties or reset existing ones them, but we don’t have any straightforward way of entirely replacing values.

This is partially because we shouldn’t really want to, with sets. The nature of the way that they are set up makes them an excellent tool for making a single source of truth where the main question to be answered is simply an on/off case of membership. If what you want is a data-set you can twist around, chop up, and replicate, arrays and objects haven’t gone anywhere. Sets are fantastic when you have unpredictable input to clean, or when you want to be able to ensure membership without worry about duplicates, particularly with primitive data types.

One last thing!

Once we’re all done with all of our set manipulation, we can always set.clear() and leave no trace that we were ever there at all.

Tag cloud