First published November 24, 2019 in ITNext

Destructuring Arrays & Objects: JavaScript ES6 Feature Series (Pt 10)

For concise variable syntax, curly braces have never been more critical

Grand library filled with people studying at tables

Introduction

The inspiration for these pieces is simple: JavaScript, and especially some of the new ES6+ releases, can be more than a little perplexing at first glance to many developers. To put it bluntly: what they once thought they understood becomes a completely new beast when another layer of syntactic sugar, designed to make our lives easier, is added.

That being said, according to Wikipedia, JavaScript still powers almost 95% of the 10 million most popular webpages today.

JavaScript only continues to play an evermore crucial part in the web, and I wanted to provide a bunch of articles and examples of ES6+ features that I use regularly, for other developers to reference.

The aim is for these pieces to be short, in-depth explanations of various improvements to the language that I hope will inspire you to write some really cool stuff using JS. Who knows, you might even learn something new along the way. 😄

For my final installment in this series, I will tackle array and object destructuring: the most concise way to pull out values and properties into individual variables with very little effort.

Destructuring sounds so simple at first...

The destructuring assignment, first introduced with ES 2015, is one of my favorite additions to the JavaScript standard syntax. As I said above, destructuring makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

This may sound simple when you first hear it, but actually putting it into effect, especially with deeply nested arrays or objects, is a little trickier to grasp. But before I get into the parts that might trip you up, let’s just talk about what destructuring looks like in both arrays, and more recently, objects.

Array destructuring

Array destructuring was introduced and finalized into the ECMAScript language before object destructuring.

And just as object and array literal expressions provide an easy way to create packages of data, at will, like so:

const a = ["alpha","beta", "gamma", "delta", "epsilon"];

The destructuring assignment of those same arrays uses similar syntax, but on the left-hand side of the assignment to define what values to unpack from the sourced variable.

Anatomy of array destructuring syntax

let b, c, more;

const a = ["alpha","beta", "gamma", "delta", "epsilon"];
[b, c] = a;

console.log(b); // "alpha"
console.log(c); // "beta"

[b, c, ...more] = ["alpha","beta", "gamma", "delta", "epsilon"];

console.log(more); // [ "gamma", "delta", "epsilon" ]

The short example above demonstrates array destructuring and using the rest pattern to assign extra values, which I wrote about here, on the array named a.

Notice that the variables b and c are wrapped in brackets ([b, c]), and are assigned to equal the a array. [b, c] = a;

When those values are called they will take the first and second elements in the array. console.log(b); // "alpha" and console.log(c); // "beta";.

The rest pattern comes in with the variable more. That variable is designed to inherit all the rest of the values in the a array by putting the rest syntax (...more) ahead of the variable name when adding it to the array. Then, when more is called, it prints out the rest of the values in its own array. console.log(more); // [ "gamma", "delta", "epsilon" ].

Alright, let’s go through some different uses for array destructuring.

Basic variable assignment

The most basic type of array destructuring is taking one array of values and assigning those values to an equal amount of new variables.

Example of basic array variable destructuring assignment

const govtBranches = [ "executive", "judicial", "legislative" ];

const [branch1, branch2, branch3] = govtBranches;

console.log(branch1); // "executive"
console.log(branch2); // "judicial"
console.log(branch3); // "legislative"

Each of the three values of govtBranches has a corresponding variable wrapped in the array brackets, branch1, branch2, or branch3 that it is assigned to.

Then, if any of those variables are called, they reflect one of the individual values from the govtBranches array.

Assignment separation from declaration

Next up is when a variable can be declared first (either using the keywords let or var, not const), and then have its value assigned via destructuring.

Example of variable declaration and then value assignment with destructuring

let agency1, agency2;

[agency1, agency2] = [ "FBI", "CIA" ];

console.log(agency1); // "FBI"
console.log(agency2); // "CIA"

As you can see, the variables agency1 and agency2 are declared as undefined variables first. Next, they’re wrapped in array brackets and assigned to the array containing the values "FBI" and "CIA". From there, each variable can be called individually, and it represents one of the values in that array.

Default values

Interestingly, you can assign variables a default value, similar to default function parameter values, in case a value unpacked from an array turns out to be undefined.

This kind of thing may happen when there’s more variables created than values in the array it’s destructuring.

Example of default values in array destructuring

let one, two;

[one="a", two="b"] = ["c"];

console.log(one); // "c"
console.log(two); // "b"

The two variables one and two are assigned the default values "a" and "b" before being assigned to the array containing the single value of "c".

And when the variable one is called after the array destructuring takes place, its value is overwritten to be "c" due to the value in the array. The value of two does not change though because the array doesn’t contain a second value, so that value, if it existed, would be undefined.

Swapping variables

Did you know that two variable values can be swapped in one destructuring assignment? They can, and it’s pretty handy that you don’t need a temporary variable to make this possible anymore.

Example swapping variable values with array destructuring

let boots = "cat";
let rocky = "dog";

[boots, rocky] = [rocky, boots];

console.log(boots); // "dog"
console.log(rocky); // "cat"

Initially, the variable boots is a "cat" and the variable rocky is a "dog", but simply by switching the order of the values in the array they’re being assigned to, their values can be swapped so boots becomes the "dog" and rocky becomes the "cat".

This is a really useful trick in certain scenarios.

Parsing an array returned from a function

Getting an array returned from a function is nothing new, but now you can destructure the values being returned to make working with them more concise.

Example parsing an array returned from a function

function color() {
  return ["red", "yellow", "green", "blue"]
}

let r, y, g;

[r, y, g] = color();

console.log(r); // "red"
console.log(g); // "green"

The color() function returns an array of colors. By destructuring the variables r, y, g against the function, those values in the array are assigned to those variables.

Ignoring some returned values

In the same vein, destructuring lets you ignore certain array values you aren’t interested in.

Example ignoring values from an array with destructuring

function ignoreColor() {
  return ["indigo", "orange", "lime"]
}

const [i, ,l] = ignoreColor();

console.log(i); // "indigo"
console.log(l); // "lime"

Simply by adding an empty space in the destructured array, you can choose not to return the value "orange" from the function ignoreColor().

You can also choose to ignore all the values from a function if you’d like (though I don’t really see much of a use case for that).

[ , , ] = ignoreColor();

Assigning the rest of an array to a variable

And I’m back again to part of what I showed in the very first array destructuring example: using the rest operator (...) to pick up any leftover values from an array.

Example assigning leftover values to a variable via array destructuring

const [commanderInChief, ...staff] = ["President", "Vice President", "Chief of Staff", "Press Secretary"];

console.log(commanderInChief); // "President"
console.log(staff); // [ "Vice President", "Chief of Staff", "Press Secretary" ]

Just as before, commanderInChief takes the first value in the array, and by using the rest syntax, ...staff takes all the remaining values in the array as a new array of its own.

Simple as that. Now let’s take a look at how destructuring works on objects.

Object destructuring

Object destructuring takes a similar tack as array destructuring, except instead of values being pulled out of an array, properties (keys) and their values can be pulled out of an object.

Here’s some examples of how that can look.

Basic object destructuring assignment

Once again, I’ll start with the most basic example of how object destructuring.

Example of basic object destructuring

const pieIngredients = { pumpkin: "1 can", pieCrust: "1 crust", spice: "2 tsp"};

const { pumpkin, pieCrust, spice} = pieIngredients;

console.log(pumpkin); // "1 can"
console.log(pieCrust); // "1 crust"
console.log(spice); // "2 tsp"

By wrapping the properties in the object pieIngredients and setting them equal to the object, each property, pumpkin, pieCrust, and spice, becomes its own variable and the value attached to it becomes the value for the new variable.

Assignment without declaration

A variable can also be assigned its value with destructuring separate from its declaration, just like you can do it with array destructuring.

Example of assigning variables after declaring the variables separately

let hobby, sports;

({hobby, sports} = {hobby: "knitting", sports: "croquet"});

console.log(hobby); // "knitting"
console.log(sports); // "croquet"

Note the parentheses ( ... ) around the assignment statement are required when using object literal destructuring assignment without a declaration.

Otherwise, the syntax is considered invalid because the syntax on the lefthand side, the {hobby, sports}, is considered a block and not an object literal. Putting the parentheses around the whole line though, clarifies the intent and makes it valid.

Assigning to new variable names

One helpful thing is that a property can be unpacked from an object and assigned to a variable with a different name than the object property.

Example reassigning destructured object properties to new variable names

const car = {speed: 110, color: "red"};

const { speed: fast, color: cherry } = car;

console.log(fast); // 110
console.log(cherry); // "red"

Here, for example, const {speed: fast} = car; takes from the object car the property named speed and assigns it to a local variable named fast.

Default values

Just like with array destructuring, variables for destructured objects can be assigned a default, in the case that the value unpacked from the object is undefined.

Example of assigning default values to object destructuring variables

const { redWine = "cabernet", whiteWine = "pinot grigio"} = { redWine: "malbec"};

console.log(redWine); // "malbec"
console.log(whiteWine); // "pinot grigio"

In this example, the variables redWine and whiteWine are assigned default values of "cabernet" and "pinot grigio". Then the redWine variable is reassigned the value of "malbec", but since whiteWine is not defined in the object being destructured, it retains its original value.

Unpacking fields from objects passed as function parameters

Another feature of object destructuring is that you can actually use destructuring syntax inside of function calls to get just those values back. Take a look at this.

Example of passing destructured object properties as function parameters

const girl = {
  name: "Paige",
  age: 30,
  eyeColor: "blue",
  hair: {
    type: "curly",
    color: "red",
    length: "shoulder-length"
  }
}

const getUserName = ({name}) => {
  return {name};
}

console.log(getUserName(girl)); // { name: "Paige" }

const getUserHair = ({hair: {type, color}}) => {
  return `Her hair is ${color} and ${type}`;
}

console.log(getUserHair(girl)); // Her hair is red and curly

In the example here, the object girl is a pretty standard one. It’s got two levels of nested properties but other than that, it’s unremarkable.

The thing to notice is the two functions getUserName() and getUserHair(). You’ll see with getUserName() the function parameter being passed to it is actually the destructured version of the property name from the object it receives.

So when the whole girl object is passed to the function, it returns just the property and value of name as the function’s output.

The second function, getUserHair() is even more interesting because the value it’s trying to access is actually located two levels down within the object being passed to the function, so first, the property of hair must be accessed, and then the properties unique to hair, which are type and color can be accessed.

That function will then return a string stating the object’s hair color and hair type as the output when it’s called with the object of girl.

This is also an example of how to access nested objects using destructuring, which I’ll talk about next.

Nested object destructuring

This was a topic that took me some time to wrap my head around (and to be honest, I usually still have to go back and look at the documentation again when I want to destructure multi-level nested objects).

The basic gist though is: if your object is more than one level deep within your object, you must first access its parent property, and its parents’ parent property, and so on, until you reach the outermost object property value.

Example of deeply nested object destructuring

const girl = {
  name: "Paige",
  age: 30,
  eyeColor: "blue",
  hobbies: {
    primaries: [
      {
        mostFavorite: [
          "drawing",
          "art"
        ],
        frequentlyDone: "cooking",
        relaxing: {
          reading: "fictionBooks"
        }
      }
    ]
  }
}

const {
  hobbies: {
      primaries: [
      {
        mostFavorite
      }
    ]
  }
} = girl;

console.log(mostFavorite[0]); // "drawing"
console.log(mostFavorite[1]); // "art"

const {
  hobbies: {
    primaries: [
      {
        relaxing: {
          reading
        }
      }
    ]
  }
} = girl;

console.log(reading); // "fictionBooks"

I used the same girl object from the previous example but I added the property of hobbies to the object and added some new arrays and objects within them so I could show how you can pull values out of either.

The first new object I create pulls out the nested object property of mostFavorite, which happens to be an array with two values. To reach these values, first, I have to wrap the outermost property of girl, which is hobbies in curly braces. Next, I have to wrap hobbies" property of primaries. Then, I must dive into both the array and the object that primaries contains to reach the mostFavorite property which actually holds the values I seek.

From there, it’s a simple exercise to log out any values that mostFavorite has.

In the same vein, to reach the value of the property reading buried deep within the girl object, I have to again start out by wrapping hobbies in curly braces, then proceed on to primaries, dive into the array and find the object property of relaxing and finally wrap the property of reading, which belongs to the parent object of relaxing.

Then, I can simply call reading as its own variable and get back the value nested deeply in the girl object.

It definitely takes some practice to get the hang of, but look at how much less syntax is needed to get those values than before.

So long const reading = girl.hobbies.primaries[0].relaxing.reading;. I won’t miss that.

If you’d like to read more about nested object destructuring, I wrote another article specifically about it a few months back, as well as ways to avoid undefined errors if values weren’t available. Here’s the link to it. 😄

Rest syntax in object destructuring

Last example, which is still in ECMAScript stage 4 proposal, I might add, at the time of writing this article: rest syntax plus object destructuring.

I showed this in the very first array destructuring demo, but I haven’t shown it with object destructuring yet. Turns out, it works about the same for objects as it does for arrays.

Example of object destructuring with the rest syntax

let myObjectOfNums = {
  ex: "ten",
  why: "twenty",
  zed: "thirty",
  dee: "forty",
  ee: "fifty"
}

let {ex, why, zed, ...allOthers} = myObjectOfNums;

console.log(ex); // "ten"
console.log(why); // "twenty"
console.log(zed); // "thirty"
console.log(allOthers); // { dee: "forty", ee: "fifty" }

See how easy it is to pull out the first three properties and their values from the object myObjectOfNums, as well as using the rest parameter to keep the other properties contained together in a new object called allOthers?

And the other rules of destructuring still apply just the same here. If you wanted to change the variable names from ex or why to a and b you could do so just the same as before.

Example of object destructuring with the rest syntax AND variable reassignment

let { ex: a, why: b, zed, ...allOthers } = myObjectOfNums;

console.log(a); // "ten"
console.log(b); // "twenty"
console.log(zed); // "thirty"
console.log(allOthers); // { dee: "forty", ee: "fifty" }

This is also totally valid. Pretty cool, huh? 😃

Conclusion

Array and object destructuring is something that's existed in languages like Perl and Python for years, but it wasn’t until ECMAScript 2015 that JavaScript began to get some parity in this area.

This new syntax utilizing curly braces makes it possible to easily access individual variables from arrays, and even objects, with very concise code. And I, for one, am a big fan of it.

My goal with this series is to examine parts of the ES6 syntax you use everyday but may not know all the subtleties and nuances of, so you can be an even better web developer.

Check back in a few weeks, I’ll be writing about more JavaScript and ES6 or something else related to web development.

Thanks for reading, I hope I’ve made array and object destructuring a little more clear and something you’re excited to try out in your own code bases.

References & Further Resources

Want to be notified first when I publish new content? Subscribe to my newsletter.