Upgrading JavaScript’s collections

Introduction

Phil Nash kicks off his talk about the fundamental elements of JavaScript, particularly focusing on new additions to collection objects.

The Power of Sets

Phil dives into the Set object, highlighting its historical use for creating unique arrays and fast membership checks. He then introduces the new set operations like union, intersection, and difference, explaining how they simplify common data manipulation tasks and replace potentially awkward code.

The Long Road to Set Operations

Phil discusses the reasons behind the long delay in introducing set operations to JavaScript, attributing it to technical challenges related to passing non-primitive objects as arguments and the impact of community involvement and standardisation processes.

Set Operations: Available Now!

Phil announces the immediate availability of set operations in major browsers and JavaScript runtimes, emphasizing their presence in recent releases like Firefox 127. He encourages developers to start utilizing these features today.

Immutability and Array Copying

Moving on to arrays, Phil addresses the concept of immutability and its importance in UI engineering. He points out the drawbacks of mutating arrays and introduces the new array copying methods like toSorted, toReversed, and toSpliced, which promote immutability and streamline array manipulation.

Grouping Data with Objects and Maps

Phil explores the new grouping methods for objects and maps, showcasing their ability to simplify the process of grouping data based on specific criteria. He compares the new object.groupBy and map.groupBy methods to previous solutions, highlighting their improved readability and conciseness.

The Future of Collections: Immutability with Records and Tuples

Looking ahead, Phil introduces the concepts of Records and Tuples, proposed immutable data structures intended to provide built-in immutability for objects and arrays. He explains their characteristics, syntax, and potential benefits, including deep immutability, value-based equality, and enhanced error handling.

Symbols, WeakMaps, and Records & Tuples

Phil explains the relationship between symbols, WeakMaps, and the proposed Records & Tuples. He illustrates how Symbols as WeakMap keys offer a mechanism to reference mutable objects within immutable data structures while maintaining garbage collection integrity.

Conclusion and Resources

Phil concludes by summarizing the discussed updates to JavaScript collections and emphasizing the continuous evolution of the language. He encourages developers to stay informed about upcoming features through resources like the TC39 proposals page and provides links to his articles for further in-depth information.

Thank you very much everyone.

Yes, it doesn't matter what time of the day is right now because it's just time to be on stage and that's all that matters.

It's absolutely great to be with you here.

And yes, I'm here to talk about a bit more of the fundamental bits of JavaScript.

In a way, it's I only feel a little bit disappointed that I got to go after Julian and he was like, here's a whole bunch of stuff the web platform can do and I'm like, cool.

I've got some arrays to tell you about.

I'm not joking, but, the thing is, JavaScript, I think, has been incredible, right?

And, over the years, it continues to evolve and change, and sometimes fundamentally change the way we write things, sometimes not.

We had this big land in ES2015, which is a horrifying number of years ago now, if you've, I the web for that long or longer, even as Ben was talking about earlier.

ES2015 is now a good solid nine years ago and gave us a whole bunch of stuff which did change the way we wrote JavaScript.

And since then, we've then had these incremental kind of yearly updates to the spec, which is really useful, although hard to keep up with.

But I do the sort of fundamental changes.

We went from writing promises, which did turn up in ES2015 and little chains of thens, which I was great at, to async await and, making things a bit more synchronous looking and happy.

And then we've had additions since then to things like, symbols as WeapMap keys.

Anybody use that yet?

No hands.

Cool.

It's profit at the end though, I think.

I'm sure it comes up.

It might, it may come up.

Keeping up the JavaScript can be a bit of a pain, and so I did, I wanted to put this together to yeah, keep us up to date with a few of the fun, new additions to, the collections objects in JavaScript, that, are going to maybe change the way you write JavaScript a little bit.

And by the end, we will have a look at the future to see, how we might change, how might, how things might change in the near ish future, to actually change, how we do write things.

So I'm going to start with sets, the set object is, I always find this really useful when doing any kind of stuff with data, and, and I work at a company called Datastacks, that works for me, the set object works for that, it turned up in 2015, incomplete?

I'd say, but we're getting to that completeness.

See, I think the set object actually at the moment gets used for two things.

For me, making arrays unique, the one use of number one use for sets, which is great because you've got an array in a set and then put it back in an array again, and you're like, yeah, done.

And the other one is, fast membership checks.

Don't look things up in an array if you don't have to, cause that's time takes as long as the array could be, whereas a set is a constant time lookup, and so that's a happy thing.

Two wonderful things.

And the reason that we only use it for two things like this is because that's all the methods we got.

Putting things in an array and checking if they're in there.

And that makes me, putting things in a set, sorry, and checking things if they're in there.

And that kind of made me sad.

But, finally, the rest of, of stuff came out, and I did a bit of maths at university, so set theory has always been somewhere in the back of my mind, and I'm like, this doesn't exist.

And so we end up writing our own kind of missing set operations, I'm sure, a bunch of the time.

If you take a couple of sets, in this case, like front end languages and back end languages, and, yeah, JavaScript is in both, Making all languages the union, of all things.

You could, spread, those two sets into an array and then put that back in a set, and then you've got all of the things in that set, but that's so many objects, and so unclear as to what we're necessarily doing, but we now have a set union operator, which is called frontendlanguages.

Union, with our backendlanguages, and we have them all.

Very exciting.

And I had to come up with SVG Venn diagrams just to show exactly how excited I was about this.

All of the things in the set are now there.

One, one method.

Ah, good.

Then, we could take our set, and I have to look at the code for this just to make sure.

We take the set, we spread it into an array, we filter out the things that are in the other one.

We filter the things so that they're the ones that are also in the other one, right?

And we get an intersection of sets.

We get the, just JavaScript because it's in both front and back end.

But we could just call, set.intersection now.

Very happy about that.

We get the things that are in the middle.

And again, we just get, we get to get rid of, obscure code.

Obscure code that, again, I have to read in order to make sure I know what's happening.

This is the opposite of that.

So this is spreading an array, set into an array, filtering it for things that aren't in the same set.

Which gives us a set, difference, so we can take one away from the other.

If we take back end languages away from front end languages, we just get HTML and CSS, the left hand side of the, thing.

And, we can now just call on set difference.

Thank you.

And then, and this is the most annoying and confusing one because it takes several lines to get through it.

If we filter out all the things that were, that are in one set, but not in the other, and then do that the same for things that were in the other set, but are in this one, and then we union those together with our originally annoying, union kind of operator, we can replace the entire thing with a symmetric difference, call, which gives us everything that's not in the same set.

Maths.

I just like this because we've, replaced a whole bunch of potentially awkward code, with, four different functions.

And then there's some set comparison operators as well we can actually call on, is subset of, is superset of, and is disjoint from to find out if, respectively sets are entirely contained within a set, or whether they contain a set entirely within them, or if they're two completely different sets.

And all of these things, yeah, are wonderful and great and coming.

And I wanted to talk about them.

They're maybe not the best, that maybe you don't use sets that often, but I think it's important to actually consider why it takes so long for something like set operations to get into a language when the set object turned up nine years ago.

And it turns out it's People.

People with very good intentions, and a slight difficulty in the language itself.

It turns out that actually these set operations are the first thing that we've put into the language kind of standard library like this, where, an object, has a function that specifically takes, a non primitive object, another set, so the union and intersection and differences, and all of those things take another set as an argument.

And if you think through all of JavaScript, I won't make you, but if you think through all of it, none of our other things necessarily really do that, and it turned out that this was a problem, in 2015, and, and that is why this, that conversation got parked by the standards committee at the time, and said we'll just get sets in so people can make unique arrays of things.

And, and then eventually we'll get back to it.

And then time and arguments and, I've read through the minutes of, some of the TC39 meetings where they tried to work out, what to do with passing a, because, the point, JavaScript being, when you parse in a set to something like an intersection is, the actual most efficient way to do an intersection is to pick the smallest set and find the ones in the other set that are also inside.

And so if you do that, you could be working with the passed in array, passed in set, passed in, set to the intersection function.

And because it's JavaScript, You can just mess with those.

You can pass in something that looks like a set, but we just pulled off the methods that we needed.

And, they had to deal with the fact that, if you had done that, I guess it's up to you to handle that.

But, yeah, don't pull methods off of basic objects.

It's a pain.

Anyway, that's why it took that and people having general lives, to, to live, giving long gaps between, TC39 meetings and things like that, led to it taking to, up to, this year to manage to get into the standards or actually, in fact, ES2025, sets have missed 2024, but they are actually already implemented in browsers.

So you can actually use these these operations today.

They just don't, they won't be in the standard until next year.

We, we apparently there was a cutoff cut over there.

And Set operations actually aside from right at the end, the newest thing I get to talk about today, even though they did in fact land in browsers, actually this week, Firefox 127 came out this week, and so I had to restart my browser whilst writing these slides in order to test things out.

Restart to update.

Oh, yeah, so you can use these set operations now, and of course, if you want to consider JavaScript on the server side as well, Node and Bun and, Deno are all following kind of the browsers anyway, because they use their internal engines anyway, so it's there too.

And, just in keeping with getting rid of Lodash, from earlier, it turns out that things like, the Lodash union package, and if you do, minor aside, if you do have a whole install of Lodash in your project, but you only use four functions, you can just install those four functions, and It's very cool.

But the Lodash Union and Lodash Difference functions on npm both hit 6.5 million downloads a week at the moment, so you can just toss them away.

Or, throw out that obscure code, your own implementation of a union or intersection if you already have one.

Next up are arrays.

Arrays, don't change that often, which I think is I guess cool, they're useful, they're lovely lists of things for us.

Hot, and has been for quite a while in, in at least, UI, engineering, is immutability.

That idea that we should pass around objects that don't change, and if we need to change something, we copy and change that copy so that we don't cause side effects elsewhere in an application.

And immutability is really useful for this, but it also leads us to writing, awkward and slightly arcane code like we've seen with the set operations If you have right when you're sorting an array This one's an awkward one I think if you have an array of stuff and you call array.sort and you hand that off to another to a sorted variable in this case then both of those resultant arrays are sorted because really we're just mutating the array but returning it at the same time, which I presume was some sort of, hack.

It was, it just doesn't seem like a good idea, and it's caused kind of confusion in code, because you might think that, when you assign, you, may be copying this array over, and you're not.

And so what you do actually is to spread an array into another array in order to copy it and then sort it, but that gives you problems if your array is like a deeply nested thing, and you're not, maybe not, copying the objects inside of it, leaving them as the same as the original ones, and uhh.

But that's the way to get around it right now.

Thankfully, people have been thinking about this and making it better over time.

And so we now have a bunch of array copying methods.

Things like toSorted, which is going to copy the array and give it back to you.

So you don't have to copy it yourself and you don't get to change, you don't have to mutate the underlying array.

That one's nice and useful.

The same happens for, reversed.

Reverse acts the same as sort, on an array.

But, toReversed will just copy and return and give you a, not touch the original array.

Like that.

ToSorted, toReversed, and then the most awkward one of all, toSpliced.

No one knows how to use splice anyway.

And it sounds like slice.

Splice, of course, allows you to give an index and a number of things, and then tell you, and then you'll start replacing things for that many number of things at that point.

So in this case, my toSpliced here is actually, is, replacing the first element with TypeScript, and only one element.

Yeah.

So splice does that, toSplice does that by copying and when, and toSpliced returns the array that you were changing underneath.

So we get back the new copy of the array with the new item or items inside of it.

But that's not actually necessarily how we spent a lot of time using splice.

Splice is useful because it actually used to return the things that got kicked outta the array and sometimes that was useful.

And if you do that, like this, we, the remove thing gives you, the array of.

Things get removed from the, from, the original object.

And you have to if you want the same behavior, you now have to go, okay, I'll use slice instead, which is annoying because the arguments to slice are different to the arguments to type, to splice.

And in slice you give it a, beginning and end index, but.

This is, if you want to copy, if you want to not mutate an underlying array, but get the things out of it that you would have got out of it with a splice before, then slice is going to be your friend, and, copying is, done for you.

Ugh, don't like it.

And then finally, just, with is a nice useful method, if you ever want to change an element of an array, you'd, in the old times, just update something with square bracket notation probably, but you can use with to do that in a copying method.

So in this case, this actually does the same as my toSpliced example, just with a single, array with the first item as TypeScript, gives you a copy of the array with TypeScript as the first item, and leaves the original array in place.

I'm not sure exactly how useful that one is, in an array, but, we might see how it might be useful later.

There are a couple caveats to these copying methods.

It turns out that, arrays are quite clever, so if you subclass an array, which I don't necessarily recommend, but if you have done, if you subclass an array, and you do a map on it, then, the actual, the response, the result of that mapping will also still be a, instance of the original, subclass.

That's not the case, with our new copying methods.

If you, toReversed, an array here, the, if you, to reverse a MyArray here, the result is actually a, just an instance of Array, not your original Array, not your original subclassed Array.

Which is a shame.

You can get it back, by simply, wrapping, that array in a sort of myArray.From, and it returns it to your original array, but it's just something you have to think about if you're subclassing, core objects like that.

But array copying support actually landed in the spec last year in ES 2023, and so has been around in these browsers for a little while now, which is great.

I really should have just gone with checkmarks, because no one knows when Chrome 110 came out.

Or if you can name when Chrome 110 came out, there's a prize for you downstairs.

Free coffee?

I don't know.

But these have been around for a while.

I believe Chrome 126 is the most recent, so that's actually been there for a good couple of years now, at least in Chrome.

That brings us then to, and we're going to see those methods again later, but I'm going to drop by objects and maps for a minute.

This is another case of being able to replace code that you've probably written at some point.

We now have grouping methods to group things into objects or maps.

And again, this actually works on arrays.

This is the sort of code you might have had in the past if I've got an array of people objects, and I wanted to group them by age Not something HR would ever tell you to do, but we're allowed to do it in JavaScript We'd probably make an array and then run over the loop over our array of, sorry, we'd make a new object.

We'd loop over an array of So we have to build our array of people and then just bucket them by whatever property or whatever kind of result that we want to.

And we have to do the awkward thing where we check whether this is already a key in the object or not, and if it is, we… This is not a lot of code, at the end of the day, we get buckets of 28 and 30, and sure.

Much easier if you can do it in one line, though.

And so calling on object, object.groupBy, passing in an array then a function which returns the kind of property that you'll group them on is so much easier.

Throw away that code, it's horrific.

And much easier.

It does come with some interesting, just a single line.

It does come with some interesting things.

The resultant object from a groupBy operation will be a null prototype object.

That's the same kind of object you can create if you call object.createNull.

Just removes strips out the prototype.

And that just means that you don't get, any of those existing methods that you might expect on a regular object.

Something like, hasOwnProperty or toString.

And if you do find yourself trying to debug an object that came out of here by printing it, it will tell you it can't.

Which is annoying.

But, what it means is that the only thing in that object are the grouped set of, objects that you, that you brought in from your array.

Useful, just.

Just something to know.

And of course, grouping into objects is great, but objects must have string keys, or symbol keys.

But maps can also be dealt with.

This allows us to use alternative keys, any sort of alternative key in your grouping.

And so in this case, if I have a bunch more people, but I've got a couple of objects stored at the top.

I've got my CEO, I've got my manager, and I've got a couple of employees who report up to them.

We can group them into a map, and, and use the, this is a reporting, this is an org chart kind of thing here where we're, grouping people by who they report to.

And if you want to then at the bottom kind of log out, we can get the people that report to the CEO.

We can pass that CEO object in using the map, that's useful.

Anything that's, yeah, any object can be a key in a map, which is cool.

And of course, if you were to try and just, look into that map using a new object that had the same stuff in it, you wouldn't get anything out of it because objects are not the same.

We'll see more of that later too.

This is another thing that, you might ask, like, why is this not an array, kind of thing?

Why could we not group, use array prototype group to group an array into a resultant object?

And that's something they did look into in the first place.

This turns out to be, another one of those cases of not breaking the web, array prototype group was a method added to arrays by, A library called sugar js, which existed to add some semantic, some extra sort of syntactic sugar to various objects, and is popular enough to have come up when people were looking into the naming of, this method, That didn't stop Safari.

Safari, did, call this array prototype group to object and group to map at one point.

Which I guess makes sense, but every other browser went, we'll do object group by and map group by and that'll be fine.

And, and that's how we ended up with it.

These, grouping methods, arrive, for this year's, spec, which is cool, and exist in all of those browsers, and it's Safari 17.

4 because Safari 16 and up to 17.

3 called it arrayPrototypeGroupToObject.

So if you do, if you want to do any, ah, we can use it in older Safari, you can look back to that object.

There.

It is a little, just aside there to say, if you are ever writing a library, just don't.

Don't hook things off the prototype of a built in object.

That's, you're going to cause problems 15 years down the line when somebody goes, what?

Why is this broken?

Actually turned out, I think they tried to use an array prototype groupBy as well and that broke the last pass Chrome extension.

And if you look through the bug reports on this, they're like, this is broken.

We don't know why.

It's 8 meg of JavaScript that's minified.

So they never went in looking into why exactly, but they just went, all right, we'll move it over, we can use objectGroupBy and mapGroupBy and that's much nicer.

And actually it turns out it has useful properties in the future as well.

This is another one of those things that there's a Lodash groupby, that gets two and a half million downloads a week.

Don't need that anymore, we can just group stuff with normal JavaScript.

And so yeah, that brings me to the future.

Those are our updates to, collections that I thought were useful, to know of and have in your back pocket when you're doing these things that would have otherwise needed a weird set of spreading, copying, extra code, extra libraries in order to handle.

We don't have to deal with that anymore.

But what could the future bring?

What could we have on the horizon, that might well change the way we write code?

I mentioned earlier that immutability is hot, but we don't have immutability in our language, at least not in these objects, in these groups, in these collections.

We can always change things.

It's the lie of const, where you can const up a new object and then just put other things in it because it's not really a constant.

Immutability would be useful, but it's something we have to enforce via our own standards.

Or libraries, or whatever.

But, in the kind of corridors of TC39, the words records and tuples get whispered once in a while.

And these are our immutable objects for the future.

They are intended to be these built in immutable objects that are sort of compound primitives.

The idea is that you can use them as lists or objects as arrays or objects, but in an immutable version that acts as though they're a primitive, much like a number is immutable or a string is immutable.

A record or tuple itself are also immutable.

The only way to change them is by copying and using that new copy.

And they have this deep immutability.

The current way that they are specced out is that a record and tuple can only contain other primitives including themselves.

Other immutable objects.

You can only put strings and booleans and symbols and, records and tuples inside a record and a tuple.

And it comes with a new syntax, which is always exciting.

And that looks a bit like this.

It's very simple.

It's a hash just in front of a, either a list, array, square brackets, or object, curly braces.

And when you do that is going to be an immutable, array or an immutable object, a tuple or a record.

This is where that with function comes into handy, because it is, because a tuple is, very much like an array.

You want to use it like an array, but you can't change items in it by using square bracket notation, you have to copy in and change it.

The with functionality, the with function comes Because they're immutable and because they're primitives, we can also, do equality by, value.

Unlike in arrays or objects, records and tuples will have equality by value.

And so if you were to use it a, tuple or a record as the key of a map, then you can create, a new tuple or record and use that to still access that existing key, which is useful.

And then finally they do, they will throw errors for you if you do try to change them.

You can pass them around basically and use them for the most part like, arrays or rec or, objects.

But they will throw errors in the time where you try and change them at all.

And then finally, again, they will also throw errors if you put in a non if you put in a mutable object.

If you try and put a date into a record, it's gonna be like, no, we cannot have that.

And that's useful too.

That is useful for the properties of being able to call these primitives and treat them as if they are single, immutable objects.

Turns out that Symbols and WeakMapKeys comes back at this point, and actually was, this is, why no one's excited about it, is because it's really just a little spec that pre ran, records and tuples, because, the, authors wanted to give, developers and sort of library developers in a way to, reference a mutable object from within a record or a tuple, and a symbol is an immutable object and a WeakMap will keep the objects in it as long as there's still a reference to the key.

And so if we use a symbol in a record or a tuple, we can write tooling around it to reach into, if we get a symbol out of one of those objects, just look it up in a WeakMap registry, and whenever we throw away the tuple or object, it will get released and garbage collected from the WeakMap.

And symbols as WeakMap keys.

Exactly.

And this is just an example of doing that kind of raw, like I would imagine there would be very tiny kind of libraries around that.

There is no support for this right now.

It, it lives in stage two of the TC39 process, which is, of a four stage process, still means it's got three stages to go, since they introduced stage 2.

7, for very good reason.

But, you'd have thought the stage process would also be immutable, but no, we can slip things in.

TC39 stage does mean 2 there's interest in it, there's excitement about it, and the next thing to do is write tests for it, to, to land it in 2.

7, before they agree to start implementing it.

But, there is a playground that you can go and check out, play around with these objects if you're interested at all.

And, of course, that comes with a, Babel plug in, and, where, you can use these in your own, applications right now.

I wouldn't necessarily recommend that because, adding extra tooling for one thing, if you're not already using Babel, but it may still change.

Stage two is far from complete.

And it certainly doesn't mean it's coming in next year.

It's just something to look at in the future.

And so those are the things we've got for collections coming up.

We've, added a bunch of extra useful things to our existing collections, and we are building towards a future of potential immutable collections.

Something that will hopefully make, yeah, will take, immutability out of the hands of developers and libraries and, enforcing with ESLint rules or whatever you need to do, to allow us to properly work with immutable objects.

So JavaScript keeps improving.

Sometimes it's hard to keep up with this.

Checking out the TC39 proposals page is actually really interesting.

And I recommend like a scan of it once in a while just to see like what's nearly ready, what's coming out.

I don't necessarily recommend reading the minutes of the meetings.

That is quite dense and, and there is a lot of context to bring with you to that.

But JavaScript keeps improving.

I have some links about these slides are also online.

There's a QR code right at the end, but these are articles I've written to give you a bit more in depth about things like the grouping methods, the copying methods, and our set methods, my personal favorites.

And yeah, that's what I've got, for you this morning.

Thanks so much for joining me.

Again, my name's Phil Nash.

I'm a developer relations engineer at DataStax, and I would love to talk to you about new bits in JavaScript and or generative AI at any time during the day.

Please come and find me.

Thank you very much.

Session Details

UPGRADING JAVASCRIPT'S COLLECTIONS

Phil Nash

Developer relations engineer
DataStax

ES2015 >>> ES2024

From Promises...

function similaritySearch(content) {
  return collection.find({}, { sort: { $vectorize: content }, limit: 5 })
    .then(cursor => cursor.toArray())
    .then(results => results.join("

"))
    .catch(error => console.error);
}

...to async/await...


async function similaritySearch(content) {
    try {
        const cursor = await collection.find({}, { sort: { $vectorize: content }, limit: 100 });
        const results = await cursor.toArray();
        return results.join("\n\n");
    } catch (error) {
        console.log(error);
    }
}

...to async/await...


async function similaritySearch(content) {
  try {
    const cursor = await collection.find({}, { sort: { $vectorize: content }, limit: 5});
    const results = await cursor.toArray();
    return results.join("

");
  } catch (error) {
    console.log(error);
  }
}

...to Symbols as WeakMap keys

// Step 1
const weak = new WeakMap();
const key = Symbol('my ref');
const someObject = { foo: 'bar' };
weak.set(key, someObject);

// Step 2
// ???

// Step 3
// Profit!

Keeping up with JavaScript

Set

What do you use Set for?

Making unique arrays

const uniqueArray = Array.from(new Set(array));

Fast membership checks

array.includes?(item); 
// O(n) 

set.has(item); 
// O(1)

Missing set operations

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);

const allLanguages = new Set([...frontEndLanguages, ...backEndLanguages]);

// => Set(5) {"JavaScript", "HTML", "CSS", "Python", "Java"}

Missing set operations: union

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);

const allLanguages = frontEndLanguages.union(backEndLanguages);

// => Set(5) {"JavaScript", "HTML", "CSS", "Python", "Java"}
    
A Venn diagram illustrating the union of two sets represented as two intersecting circles.

Missing set operations

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]); const backEndLanguages = new Set(["Python", "Java", "JavaScript"]); 

const frontAndBackLanguages = new Set([...frontEndLanguages].filter(item => { 
    return backEndLanguages.has(item); 
}));

// => Set(1) {"JavaScript"}

Missing set operations: intersection


const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);

const frontAndBackLanguages = frontEndLanguages.intersection(backEndLanguages);

// => Set(1) {"JavaScript"}
Venn diagram showing two overlapping circles, indicating the intersection of two sets.

Missing set operations

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = new Set([...frontEndLanguages].filter(item => {
    return !backEndLanguages.has(item);
}));
// => Set(1) {"HTML", "CSS"}

Missing set operations: difference

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const frontAndBackLanguages = frontEndLanguages.difference(backEndLanguages);

// => Set(1) {"HTML", "CSS"}
A Venn diagram illustrating the difference between two sets, with the left circle shaded.

Missing set operations


const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);

const onlyFrontEnd = [...frontEndLanguages].filter(item => 
    !backEndLanguages.has(item));
const onlyBackEnd = [...backEndLanguages].filter(item => 
    !frontEndLanguages.has(item));

const singleEnvLanguages = new Set([...onlyFrontEnd, ...onlyBackEnd]);

// => Set(1) {"HTML", "CSS", "Python", "Java"}
    

Missing set operations: symmetric difference

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const singleEnvLanguages = frontEndLanguages.symmetricDifference(backEndLanguages);

// => Set(1) {"HTML", "CSS", "Python", "Java"}
Illustration of two overlapping circles representing the symmetric difference between two sets.

Missing set operations: comparison

const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const markupAndStyle = new Set(["HTML", "CSS"]);

markupAndStyle.isSubsetOf(frontEndLanguages);
// => true

frontEndLanguages.isSupersetOf(markupAndStyle);
// => true

markupAndStyle.isDisjointFrom(backEndLanguages);
// => true

Why so long?

Set operations support: ES2025

Logos of Chrome, Firefox, Edge, Safari, and Opera browsers with version numbers 122, 127, 122, 17, and 108 respectively

Throw away your lodash

_.union(array1, array2);

lodash.union receives 6.5 million downloads/week

_.difference(array1, array2);

lodash.difference receives 6.5 million downloads/week

Array

Immutability

A flame emoji on either side of the word "Immutability".

Confusion

const array = ["JavaScript", "HTML", "CSS"];
const sorted = array.sort();

console.log(sorted);
// => ["CSS", "HTML", "JavaScript"]

console.log(array);
// => ["CSS", "HTML", "JavaScript"]

Unnecessary


const array = ["JavaScript", "HTML", "CSS"];
const sorted = [...array].sort();

console.log(sorted);
// => ["CSS", "HTML", "JavaScript"]

console.log(array);
// => ["JavaScript", "HTML", "CSS"]

Array copying methods: toSorted

1 const array = ["JavaScript", "HTML", "CSS"];
2 const sorted = array.toSorted();
3
4 console.log(sorted);
5 // => ["CSS", "HTML", "JavaScript"]
6
7 console.log(array);
8 // => ["JavaScript", "HTML", "CSS"]

Array copying methods: toReversed

const array = ["JavaScript", "HTML", "CSS"];
const reversed = array.toReversed();

console.log(reversed);
// => ["CSS", "HTML", "JavaScript"]

console.log(array);
// => ["JavaScript", "HTML", "CSS"]

Array copying methods: toSpliced

const array = ["JavaScript", "HTML", "CSS"];
const updated = array.toSpliced(0, 1, "TypeScript");

console.log(updated);
// => ["TypeScript", "HTML", "CSS"]

console.log(array);
// => ["JavaScript", "HTML", "CSS"]
A code snippet demonstrating array copying methods using the 'toSpliced' method.

Array copying methods: toSpliced

const array = ["JavaScript", "HTML", "CSS"];
	const updated = array.toSpliced(0, 1, "TypeScript");
	
	console.log(updated);
	// => ["TypeScript", "HTML", "CSS"]
	
	console.log(array);
	// => ["JavaScript", "HTML", "CSS"]

Splice usages


const array = ["JavaScript", "HTML", "CSS"];
const removed = array.splice(0, 1, "TypeScript");

console.log(removed);
// => ["JavaScript"]

console.log(array);
// => ["TypeScript", "HTML", "CSS"]

Copy with slice


const array = ["JavaScript", "HTML", "CSS"];
const removed = array.slice(0, 1);

console.log(removed);
// => ["JavaScript"]

console.log(array);
// => ["JavaScript", "HTML", "CSS"]

Array copying methods: with

const array = ["JavaScript", "HTML", "CSS"];
array[0] = "TypeScript";

console.log(array);
// => ["TypeScript", "HTML", "CSS"]

Array copying methods: with

const array = ["JavaScript", "HTML", "CSS"];
const updated = array.with(0, "TypeScript");

console.log(updated);
// => ["TypeScript", "HTML", "CSS"]

console.log(array);
// => ["JavaScript", "HTML", "CSS"]
  

Array copying methods: caveats

class MyArray extends Array {};
const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");

const upcasedLanguages = languages.map(language => language.toUpperCase());
console.log(upcasedLanguages instanceof MyArray);
// => true

const reversed = languages.toReversed();
console.log(reversed instanceof MyArray);
// => false

Array copying methods: caveats


class MyArray extends Array {};
const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");
const reversed = MyArray.from(languages.toReversed());
console.log(reversed instanceof MyArray);
// => true

Array copying support: ES2023

Logos of Chrome (version 110), Firefox (version 115), Edge (version 110), Safari (version 16), and Opera (version 96) indicating their respective versions for array copying support.

Object and Map

Grouping into an Object

const people = [ { name: "Alice", age: 28 }, { name: "Bob", age: 30 }, { name: "Eve", age: 28 } ];

const peopleByAge = {};

people.forEach((person) => { 
   const age = person.age; 
   if (!peopleByAge[age]) { 
      peopleByAge[age] = []; 
   } 
   peopleByAge[age].push(person); 
}); 

console.log(peopleByAge); 

/* 
{ 
   "28": [{"name":"Alice","age":28}, {"name":"Eve","age":28}], 
   "30": [{"name":"Bob","age":30}] 
}
*/

Grouping into an Object: groupBy

const people = [
  { name: "Alice", age: 28 }, { name: "Bob", age: 30 }, { name: "Eve", age: 28 }
];
const peopleByAge = Object.groupBy(people, (person) => person.age);
console.log(peopleByAge);
/*
{
  "28": [{"name":"Alice","age":28}, {"name":"Eve","age":28}],
  "30": [{"name":"Bob","age":30}]
}
*/

Grouping into an Object: null-prototype object

const people = [
    { name: "Alice", age: 28 }, { name: "Bob", age: 30 }, { name: "Eve", age: 28 }
];

const peopleByAge = Object.groupBy(people, (person) => person.age);

console.log(peopleByAge.hasOwnProperty("28"));
// TypeError: peopleByAge.hasOwnProperty is not a function

Grouping into an Map: groupBy

const ceo = { name: "Jamie", age: 40, reportsTo: null };
const manager = { name: "Alice", age: 28, reportsTo: ceo };

const people = [
  ceo,
  manager,
  { name: "Bob", age: 30, reportsTo: manager },
  { name: "Eve", age: 28, reportsTo: ceo },
];

const peopleByManager = Map.groupBy(people, (person) => person.reportsTo);

console.log(peopleByManager.get(ceo));
/* => [
  { name: "Alice", age: 28, reportsTo: ceo },
  { name: "Eve", age: 28, reportsTo: ceo }
] */

Grouping into an Map: groupBy

peopleByManager.get({ name: "Jamie", age: 40, reportsTo: null });
// => undefined
    

Naming shenanigans

Or why is it Object.groupBy and not Array.prototype.group?

Array grouping methods: ES2024

Icons of Chrome, Firefox, Edge, Safari, and Opera browsers with version numbers underneath: 117, 119, 117, 17.4, and 103 respectively.

Throw away your lodash

_.groupBy(people, person => person.age);
lodash.groupby receives 2.5 million downloads/week

The future

@phinash
Image of a purple crystal ball

Immutability

Records and Tuples

Records and Tuples

  • Built-in immutable objects
  • Compound primitives
  • Deep immutability
  • New syntax

Records and Tuples

const tuple = #[1,2,3];
const record = #{ name: "Alice", age: 28 };

Records and Tuples

const tuple = #[1,2,3];
const record = #{ name: "Alice", age: 28 };

const newTuple = tuple.with(0, 4);
// => #[4,2,3]

Records and Tuples

const tuple = #[1,2,3];
const record = #{ name: "Alice", age: 28 };

const newTuple = tuple.with(0, 4);
// => #[4,2,3]

record === #{ age: 28, name: "Alice" };
// => true

Records and Tuples

const tuple = #[1,2,3]; 
const record = #{ name: "Alice", age: 28 };
const newTuple = tuple.with(0, 4); 
// => #[4,2,3]
record === #{ age: 28, name: "Alice" }; 
// => true
record["name"] = "Betty"; 
// TypeError '"name" is read-only'
const recordWithObject = #{ name: "Betty", dob: new Date(1984, 8, 14) }; 
// TypeError 'cannot use an object as a value in a record'

Symbols as WeakMap keys

const registry = new WeakMap();
const dob = Symbol('dob');
registry.set(dob, new Date(1984, 8, 14));

const record = #{ name: "Betty", dob: dob };

console.log(registry.get(record.dob));
// => "Fri Sep 14 1984"

Records and Tuples support: TC39 stage 2

Logos of Chrome, Firefox, Edge, Safari, and Opera browsers, each with a red cross mark below them.

Records and Tuples

Check out the playground:

https://rickbutton.github.io/record-tuple-playground/

Thanks

twitter.com/philnash
linkedin.com/in/philnash
philna.sh/links

A QR code, a headshot of a smiling man with glasses, and the Datastax logo.