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 arrayrgb
,alpha
isundefined
.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 andalpha
retains its default value of 0.2 since it doesn't have a corresponding element in the arrayrgb
.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 thered
,green
, andblue
values and leavealpha
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 ofplanetsInTheSolarSystem
. 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