Destructuring
Tags: javascript, webdev, beginners, tutorial
Published: 2021-10-05 17:16:21

*This article is part of the first installment of a series I'm calling __JavaScript You Must Know__. The aim of this series is to go over various fundamentals of the JavaScript language that a developer must know/understand before moving forward with any of the modern JavaScript frameworks like React, Angular, and Vue. Learning these fundamentals in parallel with the frameworks would also be acceptable.*

*This first installment of the series focuses on syntax-related topics and is called __JavaScript You Must Know -- Essential Syntax__.*

---

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.

```js
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:

```js
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:

```js
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.

```js
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.

```js
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`.

  ```js
  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`.

  ```js
  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.

  ```js
  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.

  ```js
  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.
> ```js
> 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.

```js
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).

```js
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:

```js
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).

```js
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:

```js
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.

```js
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.

```js
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:

```js
{ 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](http://randomuser.me). The API returns a `results` array within a JavaScript object which looks like this:

```js
{
  "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.

```js
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.

```js
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`.

```js
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();
```

---

👉🏻 Subscribe to my newsletter: [click here](https://www.bracketsinstitute.com/newsletter-subscription)

👉🏻 Follow me on twitter: [click here](https://twitter.com/bi_nrf)

---