Destructuring objects and arrays in JavaScript

Feb 03, 2019

Destructuring

Destructuring assignment

The destructuring assignment allows you to assign items of arrays or properties of objects to separate variables. Let's look into more detail how this works.

Array destructuring

Let's assume we have an array and we want to assign its contents into separate variables. Without destructuring, you would need to do something like this:

let myArray = [1, 2, 3]; 
let a = myArray[0];
let b = myArray[1];
let c = myArray[2];

With array destructuring assignment, it is much easier:

let myArray = [1, 2, 3];
let [a, b, c] = myArray; // a=1, b=2, c=3

Much better, right? The first item from array gets assigned to the first variable, the second item in the array to the second variable and so on.

In the example above, we declared new variables and assigned to them, but you can use existing ones.

Of course you don't need to use all the items from the source array:

let myArray = [1, 2, 3];
let [a, b] = myArray; // a=1, b=2

Default values

On the other hand, if there are not enough items in the array, only some of the variables get assigned. The rest remains undefined as if you would declare a variable and not assign it.

let myArray = [1];
let [a, b, c] = myArray; // a=1, b=undefined, c=undefined

You can provide default values to the variables in case there is not enough items, so they have some value as a fallback:

let myArray = [1];
let [a=2, b=4, c=6] = myArray; // a=1, b=4, c=6

Because it is called destructuring, you might think that items are actually removed from the source array. This is, however, not the case, the source array remains unchanged. The same also applies to destructuring objects.

Ignoring items

If you want to skip certain items, you can do it like this:

let myArray = [1, 2, 3];
let [a, , b] = myArray; // a=1, b=3

Swapping variables

The traditional approach to swapping variables involves using a third temporary variable:

temp = a;
a = b;
b = temp;

With destructuring, variable value swap is as easy as this:

[x, y] = [y, x];

Assigning the rest of the items

You can assign just a first few items from the source array and put all the unassigned items in a new array:

let myArray = [1, 2, 3, 4, 5, 6];
let [a, b, ...others] = myArray; // a=1, b=2, others = [3, 4, 5, 6]

The ... syntax with its various uses is described in detail in the following article:

Javascript spread operator and rest parameters (...)

What do three dots (...) mean in javascript? They can be used for various different purposes.

Nested arrays destructuring

When you have an array, which contains other arrays, you can still use destructuring to get to the items in the nested array:

let myArray = [1, 2, [3, 4], 5];
let [first, second, [third, fourth], fifth] = myArray;
// first = 1, second = 2, third = 3, fourth = 4, fifth = 5

Destructuring iterables

All the examples above used arrays as a source for destructuring. Actually, this is just a special case as you can use any iterable such as string. In that case each character gets assigned to a single variable:

const person = "John Doe";
let [first, second] = person; // first = "J", second = "o"

Or you can even use regular expression matches:

let pattern = /(\w+)/g; // Match whole words
let string = "The quick brown fox jumps over a lazy dog.";
let results = string.match(pattern);
// results = ["The", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"]
let [first, second] = results; // first = "The", second = "quick"

Object destructuring

Object destructuring works in a similar way to array destructuring with a few distinctions.

// Simple assignment
let john = {name: "John Doe", age: 42, gender: "Male"};
let {name, age} = john;

// Default values
let {name, age, favoriteColor = "Black" } = john; 

// Other unused properties assigned to a new object
let {name, age, ...others} = john;

Changing variable names

Unlike arrays, where the assignment is determined by order, here it is by variable name matching object's property name. That means that is assigning to a variable called name it will use value of a property name from the source object.

However, if you want, you can make the name different if you want:

let john = {name: "John Doe", age: 42};
let {name: newName, age: newAge} = john;
// newName ="John Doe", newAge = 42

That can be particularly useful when working with object property names, which are not valid variable names. For example, this is a valid object:

{"my-property": 42}

But my-property is not a valid variable name. Fortunately, we can assign different variable name, which is valid using the approach above:

// Not valid syntax: my-property is not valid variable name
// But it is valid object property name
let {my-property} = {"my-property": 42};

// But we can change my-property to valid variable name
let {"my-property": myProperty} = {"my-property": 42};
// myProperty = 42

Dynamic property names

When working with plain objects, you can access their properties by property name like person.name or person["name"]. What's more interesting, you can also use a variable in place of property name - person[myVariable]:

let person = {name: "John", age: 42, hobby: "Javascript"};

let propertyKey;
if(something) {
    propertyKey = "age";
} else {
    propertyKey = "name";
}

let foo = person[propertyKey]; // foo = 42 or "John"

You can use similar approach when destructuring:

let person = {name: "John", age: 42, hobby: "Javascript"};

let propertyKey;
if(something) {
    propertyKey = "age";
} else {
    propertyKey = "name";
}

let {[propertyKey]: foo} = person; // foo = 42 or "John"

Usage in iteration

Destructuring can be useful when iterating over multiple objects. You can easily extract just the properties you are interested in:

let persons = [{name: "John", age: 42, hobby: "Javascript"}, {name: "Jane", age: 24}];

for(let {name, age, hobby = "Unknown"} of persons) {
    console.log(name);
    console.log(age);
    console.log(hobby);
}

It is not only concise when accessing the current object's properties, but also it allows you to define default values of missing properties easily.

Assignment to existing variables

When destructuring arrays, it is not different whether you declare your variables and immediately assign to them or whether you assign to previously declared variables:

// Declaration and assignment
let [foo, bar] = [1, 2];

// Declaration and assignment separately
let foo, bar;
[foo, bar] = [1, 2];

With objects it is different:

// Declaration and assignment: perfectly fine
let {foo, bar} = {foo: 1, bar: 2};

//SyntaxError: Unexpected token =
let foo, bar;
{foo, bar} = {foo: 1, bar: 2};

What happened here? Curly braces in {a, b} get interpreted as a declaration of a block rather than object destructuring assignment. Fortunately, you can wrap the assignment in regular braces to make it work:

({foo, bar} = {foo: 1, bar: 2});

Nested object destructuring

Similar to nested array destructuring, you can destructure even items in the nested objects of the source object:

let john = {
        name: "John Doe",
        age: 42,
        skills: {
            cooking:9,
            javascript: 1,
        }
};

let {age, name, skills: {cooking, javascript}} = john;
// name = "John Doe", age = 42, cooking = 9, javascript = 1

Of course, when your object contains arrays as property values, you can destructure them as well. You can even combine both:

let john = {
    name: "John Doe",
    age: 42,
    skills: {
        cooking:9,
        javascript: 1,
    },
    hobbies: ["Soap operas", "Internet trolling"]
};

let {age, name, skills: {cooking, javascript}, hobbies: [hobby1, hobby2]} = john;
// name = "John Doe", age = 42, cooking = 9, javascript -=1
// hobby1 = Soap operas, hobby2 = Internet trolling

Destructuring function parameters

Imagine you have a function, which instead of various individual parameters accepts an object with multiple properties:

const myImage = {
    x: 100,
    y: 200,
    source: "http://example.com/image.png",
    grayscale: false
};

function drawImage(imageConfig) {
    console.log("Drawing image:");
    console.log("Position: " + imageConfig.x + "," + imageConfig.y);
    console.log("Source image location: " + imageConfig.source);
    console.log("Grayscale: " + imageConfig.grayscale);
}

drawImage(myImage);

This is often useful, especially if you have many configuration properties and some of them are optional. However, it has some disadvantages. You need to access all the properties through the source object like this: imageConfig.grayscale. Alternatively, you need to declare variables and assign properties to them. With optional properties, it is even worse as you need to handle manually default values if a property is not present. Fortunately, you can use object destructuring to create variables from the input object:

function drawImage({x, y, source, grayscale}) {
    console.log("Drawing image:");
    console.log("Position: " + x + "," + y);
    console.log("Source image location: " + source);
    console.log("Grayscale: " + grayscale);
}

But of course, it is even more useful with default values:

function drawImage({x = 0, y = 0, source, grayscale = false}) {
    console.log("Drawing image:");
    console.log("Position: " + x + "x" + y);
    console.log("Source image location: " + source);
    console.log("Grayscale: " + grayscale);
}

Conclusion

Destructuring is a useful tool which allows you to break down complex structures such as arrays and objects to simple parts. The syntax is much more concise than the traditional approach, especially when handling situations such as default values.


Tagged with:
Vojtech Ruzicka

Get notifications about new posts on Twitter, RSS or Email.