Destructuring

Destructuring

ยท

11 min read

Destructuring, also called destructuring assignment, is when individual elements of an iterable are extracted and assigned to variables. By far the most common use cases for it are with Arrays and Objects.

Array Destructuring


Let's say we have an array, rgba, which defines the red, green, blue, and alpha values.

const rgba = [100, 100, 0, 0.2];

We wish to extract thsoe values and store them in individual variables called red, green, blue, and alpha. The old-school way to do this would be something like this:

const rgba = [100, 100, 0, 0.2];
const red = rgba[0];
const green = rgba[1];
const blue = rgba[2];
const alpha = rgba[3];

console.log(red, green, blue, alpha); // output: 100 100 0 0.2

With destructuring, it would be:

const rgba = [100, 100, 0, 0.2];
const [red, green, blue, alpha] = rgba;

console.log(red, green, blue, alpha); // output: 100 100 0 0.2

Note the square brackets on the left hand side of the assignment at line 2. These square brackets signify that a destructuring assignment is being requested. Once it is determined that a destructuring is required, the expected value on the right hand side of the assignment is an iterable; which in our case is an array.

With array destructuring, the values of an array are assigned to the variabes from left to right. This means that the value at index 0 will be assigned to the first variable, the value at index 1 will be assigned to the second variable, and so on. If a value is not required to be stored, we can simply skip naming it and move on.

const rgba = [100, 100, 0, 0.2];
const [red, green, , alpha] = rgba;

console.log(red, green, alpha); // output: 100 100 0.2

In the above example, we deleted the variable blue but didn't change the positions of any of the other variables. That's why at the end we only have red, green, and alpha declared with the correct values.

The right hand side of the assignment can be the array literal itself. The following is perfectly valid and would give the same results as our second-last example above.

const [red, green, blue, alpha] = [100, 100, 0, 0.2];

Left hand side has more variables

In case the left hand side of the assignment has more variables than the elements of the array:

  • The extra variables will be declared but will have no value assigned to them. Therefore they will be undefined. In the example below, since a 4th element doesn't exist in the array rgb, alpha is undefined.

    const rgb = [100, 100, 0];
    
    // left hand side of the assignment below has more variables than elements in the array
    const [red, green, blue, alpha] = rgb; 
    
    console.log(red, green, blue, alpha); // output: 100 100 0 undefined
    
  • The variables can be assigned a default value. If a corresponding element is available on the right hand side, the variable's value will be updated. Otherwise, the default value will be used. In the next example, green is set to 100 (even though a default value is present) via destructuring assignment and alpha retains its default value of 0.2 since it doesn't have a corresponding element in the array rgb.

    const rgb = [100, 100, 0];
    const [red, green=125, blue, alpha=0.2] = rgb;
    
    console.log(red, green, blue, alpha);
    

Right hand side has more variables

In case the right hand side of the assignment has more elements than the variables:

  • If there are more elements in the array than the variables being declared, then the assignment will be carried out normally and the extra elements of the arary will have no effect whatsoever. As an example, we'll again take the rgba array but only extract the red, green, and blue values and leave alpha alone.

    const rgba = [100, 100, 0, 0.2];
    const [red, green, blue] = rgba;
    
    console.log(red, green, blue); // output: 100 100 0
    
  • Finally, we can use the rest syntax to gather any and all remaining values neatly into an array. Notice, in the example below, that restOfThePlanets is an actual array with all the remaining elements of planetsInTheSolarSystem. This is the same as using rest parameters to gather all the arguments to a function into an array.

    const planetsInTheSolarSystem = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]; // is Pluto a planet?
    const [firstPlanet, secondPlanet, thirdPlanet, ...restOfThePlanets] = planetsInTheSolarSystem;
    
    console.log(firstPlanet, secondPlanet, thirdPlanet, restOfThePlanets);
    // output: "Mercury" "Venus" "Earth" ["Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
    

๐Ÿ’ก just a tip

Array destructuring can be used to easily swap the values of two variables.

let a = 10;
let b = 20;
[a, b] = [b, a];

console.log(a, b); // output: 20 10

Object Destructuring


When it comes to objects, the destructuring assignment has one very important difference from array destructuring: with objects, the variables on the left hand side of the assignment are used to look-up the values from the object on the right hand side (the order doesn't matter). Also, curly braces {} are used instead of square brackets [] to signal an object destructuring.

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

const { id, name } = studentRecords;

console.log(`${id}: ${name}`); // output: "107: Plump Sunny"

Let's go through the destructuring in detail. The curly braces {} on the left hand side of the assignment signal that an object destructuring is requested and, this automatically implies, that there should be an object on the right hand side of the assignment. Next, the first variable is called id, the JavaScript engine will look for a key called id in the studentRecords object. If id is found in studentRecords, its value will be assigned to the id variable. If id is not found, then the id variable will be undefined. The same process is followed for the name variable.

Object destructuring is very useful and is used by programmers extensively. And for good reason too as it makes the code very concise. Note that the order in which the variables appear is not important here (as opposed to array destructuring where the order is important).

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

const { name, id } = studentRecords;

console.log(`${id}: ${name}`); // output: "107: Plump Sunny"

In the example above, even though we swapped the id and the name variables on the left hand side of the assignment, each variable still gets the correct value from the studentRecords object.

Since the variable names are used to look-up the values from an object, one might think if we can rename the variables when destructuring an object. The answer is that we can by using the following syntax:

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

const { physics: phy, chemistry: chem, mathematics: maths } = studentRecords;

console.log(`Physics: ${phy}
Chemistry: ${chem}
Mathematics: ${maths}`);

Note that the variables that got declared in the end are called phy, chem, and maths.

[console output]:

Physics: A
Chemistry: B+
Mathematics: A+

An easy way to remember the syntax is to imagine talking to the JavaScript engine like: "Hey JS, get physics as phy, chemistry as chem, and mathematics as maths from the object studentRecords."

Just like with arrays, we can provide default values to the variables as well as use the rest syntax. The only difference is that using the rest syntax with object will return an object rather than an array (which makes sense).

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

// using default value for 'age'
// using the rest syntax to initialize 'grades'
const { id, name, age="N/A", ...grades } = studentRecords;

console.log(`${id}: ${name} / Age: ${age}
Grades: ${JSON.stringify(grades)}`);

[console output]:

107: Plump Sunny / Age: N/A
Grades:
{'physics':'A','chemistry':'B+','mathematics':'A+'}

Here age was assigned "N/A" because no key called age was found in the studentRecords object so the default value of age was used. Moreover, notice that grades is an object that contains all the remaining key/value pairs from studentRecords. Got a bit fancy here by using JSON.stringify(). All this function does is take a valid JavaScript object and returns the same object as a string that can be printed.

Use in function parameters

One of the most useful uses of object destructuring is in function parameters. Let's first understand the problem. Imagine there is a function that expects, as its arguments, a student's id and that student's grades in all the different classes. The end result might look something like this:

function wantStudentDataForSomeReason(id, physics, chemistry, mathematics) {
  // todo: do something with all that data
}

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

// time to call our function
wantStudentDataForSomeReason(studentRecords.id, studentRecords.physics, studentRecord.chemistry, studentRecord.mathematics);

It is easy to see that calling wantStudentDataForSomeReason() is a chore. Not only did we have to input each of the values individually, we also had to make sure that the values are in the same order as in the function's definition. Furthermore, what if one or more values was not available in the studentRecords object!

A nifty trick to avoid all of this trouble is to use object destructuring right in the parameters list of the function.

function wantStudentDataForSomeReason({ id, physics, chemistry, mathematics }) {
  // todo: do something with all that data
}

Notice that the parameters are not individual variables any more but are surrounded by curly braces. Now, if an object is passed as an argument to this function, object destructuring will take place and the variables will get the correct values if they exist.

function wantStudentDataForSomeReason({ id, physics, chemistry, mathematics }) {
  // todo: do something with all that data
}

const studentRecords = {
  id: 107,
  name: "Plump Sunny",
  physics: "A",
  chemistry: "B+",
  mathematics: "A+"
}

// time to call our function
wantStudentDataForSomeReason(studentRecords);

Once again, the function call in the last line of the code example above will basically resut in the following destructuring assignment to take place:

{ id, physics, chemistry, mathematics } = studentRecords

Truly an amazing use of destructuring. Now we don't have to refer to the list of parameters in the function's definition and the order doesn't matter as well. As for the missing values, any missing value will automatically be assigned undefined.

Use in APIs

APIs usually return a lot of data and, more often than not, the application does not need all of the data returned by the API. Instead of keeping the whole object returned by the API, we can used destructuring and keep only what we need.

As an example, let's consider the API of randomuser.me. The API returns a results array within a JavaScript object which looks like this:

{
  "results": [
    {
      "gender": "male",
      "name": {
        "title": "mr",
        "first": "brad",
        "last": "gibson"
      },
      "location": {
        "street": "9278 new road",
        "city": "kilcoole",
        "state": "waterford",
        "postcode": "93027",
        "coordinates": {
          "latitude": "20.9267",
          "longitude": "-7.9310"
        },
        "timezone": {
          "offset": "-3:30",
          "description": "Newfoundland"
        }
      },
      "email": "brad.gibson@example.com",
      "login": {
        "uuid": "155e77ee-ba6d-486f-95ce-0e0c0fb4b919",
        "username": "silverswan131",
        "password": "firewall",
        "salt": "TQA1Gz7x",
        "md5": "dc523cb313b63dfe5be2140b0c05b3bc",
        "sha1": "7a4aa07d1bedcc6bcf4b7f8856643492c191540d",
        "sha256": "74364e96174afa7d17ee52dd2c9c7a4651fe1254f471a78bda0190135dcd3480"
      },
      "dob": {
        "date": "1993-07-20T09:44:18.674Z",
        "age": 26
      },
      "registered": {
        "date": "2002-05-21T10:59:49.966Z",
        "age": 17
      },
      "phone": "011-962-7516",
      "cell": "081-454-0666",
      "id": {
        "name": "PPS",
        "value": "0390511T"
      },
      "picture": {
        "large": "https://randomuser.me/api/portraits/men/75.jpg",
        "medium": "https://randomuser.me/api/portraits/med/men/75.jpg",
        "thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg"
      },
      "nat": "IE"
    }
  ],
  "info": {
    "seed": "fea8be3e64777240",
    "results": 1,
    "page": 1,
    "version": "1.3"
  }
}

That is a lot of data. Let's say that we only need the gender, email, and the cell from the returned data. We can use object destructuring on the returned data and extract only what we need.

async function randomProfile() {
  const response = await fetch("https://randomuser.me/api/");
  const data = await response.json();
  const { cell, email, gender } = data.results[0];

  console.log(cell, email, gender);
}

randomProfile();

Let's take this a step further and extract the first and last name from the data. Take some time to look at how the data given to us by the API is structured. Note that there is another object inside of our results[0] object called name which in turn has the keys first and last for the first and last name. To extract data from a nested object like this, we have to tell the JavaScript engine that we're looking for a nested object.

async function randomProfile() {
  const response = await fetch("https://randomuser.me/api/");
  const data = await response.json();
  const { cell, email, gender, name: { first, last } } = data.results[0];

  console.log(cell, email, gender, first, last);
}

randomProfile();

We added this part to our destructuring assignment: name: { first, last }. By adding this, we're basically telling the JavaScript engine to look for an object within the current object called name and extract the values of the keys first and last. Notice that in the end the declared variables are first and last and not name.first and name.last.

async function randomProfile() {
  const response = await fetch("https://randomuser.me/api/");
  const data = await response.json();
  const { cell, email, gender, name: { first: firstName, last: lastName } } = data.results[0];

  console.log(cell, email, gender, firstName, lastName);
}

randomProfile();

๐Ÿ‘‰๐Ÿป Follow me on twitter: click here

๐Ÿ‘‡๐Ÿป Subscribe to my newsletter


ย