Coding

ES6 FTW: Destructuring

I recently wrote a small Slack integration for my gaming group. I wanted to handle dice rolls for tabletop games, so I needed to match patterns like 1d8, d20+2, or 2d6-4. I wrote a simple regex to handle it, which could probably be greatly improved, but works for my use case: /(\d+)?d(\d+)([\+\-]\d+)?/i (you can play with it on RegExr).

With ES5, I probably would have done something like this with the regex match results:

[code lang=javascript]
var matches = rollString.match(rollRegex);
var count = matches[1] || 1;
var die = matches[2];
var modifier = matches[3] || 0;
[/code]

With ES6’s destructuring syntax, it becomes much cleaner (and in fact, there’s actually a section doing just this sort of thing in the docs):

[code lang=javascript]
let matches = rollString.match(rollRegex);
let [, count=1, die, modifier=0] = matches;
[/code]

Two lines for four! Once you’re used to it, destructuring is pretty intuitive, but for the sake of explanation, let’s go over what just happened: we used essentially the same syntax that we use for creating an array, but on the left-hand side of the expression, to pull our target apart (or “destructure” it) and extract individual elements into named variables for further use. We started with a matches array that looked (assuming that we had an input string of d20+2) like this: ['d20+2', undefined, '20', '+2'], and “reached inside” to pull out the values that we were interested in (count, die, and modifier), ignoring the one that we didn’t care about (the whole input string, d20+2).

Additionally, since count and modifier have sensible default values (if you don’t specify the number of dice, we assume that you’re rolling a single one, and if you don’t specify a modifier, we assume that it’s 0), we were able to specify those within the destructuring expression, in case they were undefined in the array. In the above case, the count was undefined, and so defaulted to 1, but the modifier was +2, and so did not receive its default value.

In the same way that you can destructure arrays, you can also destructure objects. For example, Slack sends a payload to my HTTP endpoint (running a small express server) that comes to me looking something like this (some fields omitted for brevity):

[code lang=json]
{
"channel_name": "roller-test",
"user_name": "Problematic",
"command": "/roll",
"text": "d20+2"
}
[/code]

Assuming that I was interested in the user_name and text fields, I could grab those with the following destructuring assignment:

[code lang=javascript]
let {user_name, text} = req.body;
[/code]

If I wanted to assign those properties to variables named username and roll instead of their defaults, the syntax is slightly different, but still straightforward:

[code lang=javascript]
let {user_name: username, text: roll} = req.body;
[/code]

As a final example, you can also destructure nested data. The following snippet will assign “What is 42?” to question and “The answer to life, the universe, and everything” to correctAnswer:

[code lang=javascript]
let data = {
question: 'What is 42?',
answers: ['6 times 7', 'The answer to life, the universe, and everything', 'How many engineers it takes to change a lightbulb']
};
let {question, answers: [,correctAnswer,]} = data;
[/code]

Those are the basics of destructuring in ES6! If you want to know more, I encourage you to check out MDN, or, if you like deeper dives, the ES6 specification on the subject.