Destructuring in Javascript

Destructuring

Lets try Object destructuring.

Lets dive right in.

/* old-assignment.js */

/* Assume you have a object like this */

let user = {
    firstName: "Tom",
    lastName: "Cruise",
    email: "tomcruise@gmail.com"
}

let firstName = user.firstName;
let lastName = user.lastName;
let username = user.email;
let gender = user.gender || 'Unknown'

/* do validations, etc */

let fullName = firstName + ' ' + lastName;

Have you ever done things like this?

If you did, let's see the modern way doing the same things(i.e, destructuring).


/* modern-destructuring.js */

/* Add the above user object here */

let { 
  firstName,
  lastName,
  email: username,
  gender = 'Unknown'
} = user;

/*
 Here,

 firstName = 'Tom'
 lastName = 'Cruise'
 username = 'tomcruise@gmail.com'
 gender = 'Unknown'
 */

It's pretty, isn't it?

But there's a catch here, if we want to destructure another object to the same variables, in other words if we want to update the existing values with new object values. we will have a problem here.

/* modern-destructuring.js */

/*
Assume we have the above destructuring values here. i.e, firstName, lastName, username, gender were already initialized.
*/

let newUser= {
  firstName: 'Dwayne',
  lastName: 'Johnson',
  email: 'dwaynejohnson@gmail.com',
  gender: 'Male',
  age: 47
}

/*
We won't be able to use the exact syntax we used above
let { 
  firstName,
  lastName,
  email: username,
  gender = 'Unknown'
} = user;

Because we already declared these variables, we cannot declare again.

And,

We won't be able use the following syntax without `let` also.
{ 
  firstName,
  lastName,
  email: username,
  gender = 'Unknown'
} = user;

*/

/* Here is the correct way of doing things */
({ 
  firstName, /* Dwayne */
  lastName, /* Johnson */
  email: username, /* dwaynejohnson@gmail.com */
  gender = 'Unknown' /* Male */
} = newUser);
/*
If we want age also in a variable we won't able to do that in above destructuring like this.
({ 
  firstName,
  lastName,
  email: username,
  gender = 'Unknown',
  age
} = newUser);
Because age not yet declared. Therefore we need to declare age seperately.
*/

let { 
  age, /* 47 */
  height /* undefined */
} = newUser;

I think that's pretty much it for object destructuring. Let me know if miss anything in the comments.

Lets go for array destructuring now.


/* old-assignment.js */

let array = [4, 6, 3, 7, 5];
/* 
Assume these are the # of hours you worked for this week(Mon-Fri).
Remember how we used to assign these to each day variable.
I'm not sure about you, but I would do like this(we are sure that there will be 5 elements).
*/

let monday = array[0];
let tuesday = array[1];
let wednesday = array[2];
let thursday = array[3];
let friday = array[4];

I'm not saying it's bad way of doing things, but let's see the modern way these kinda assignments.


/* modern-destructuring.js */

/* Add the above work hours array here */

let [
  monday, /* 0th index value i.e 4 */
  tuesday, /* 1th index value i.e 6 */
  wednesday, /* 2th index value i.e 3 */
  thursday, /* 3th index value i.e 7 */
  friday /* 4th index value i.e 5 */
] = array;

/* 
And all the remaining assignments like 
reassignment,
default assignment
will work same as object destructuring,
Try it on your own.
 */

Rest(...) & Spread (...) Opearators

Have you ever seen/used these operators?

Rest refers to rest of the elements or remaining elements in object or array.

Spread refers to spread or unwind the first level of elements in object or array.

Enough theory.


/* old-assignment.js */

user =  {
  reqTime: 1587722554595,
  firstName: 'Chris',
  lastName: 'Hemsworth',
  email: 'chrishemsworth@gmail.com'
};

/*
Here don't need reqTime property in user object, it's just for logging purpose.
You know the old way of removing one/multiple property(ies) from the object?
*/

/* log and delete */
delete user.reqTime;

/*

If there are many?
then
delete user.p1;
delete user.p2;
delete user.p3;
...

*/

Here comes the modern operator Rest


/* spread-rest-play.js */

/* Add the above user object here */


let {reqTime, ...filteredUser} = user;
user = filteredUser;

/*
Here the above ...filteredUser refers to the rest of the properties in user object.
If there are multiple properties

let {reqTime, x1, x2, xn, ...filteredUser} = user;
*/

/* Lets try rest with arrays */

array = [3,5,7];

let [i1, ...rest] = array;

/*
Here
i1 = 3
rest = [5,7]

You can try different things here
Like 
what happens if we assign variables to all the properties in rhs object/array and if we have the rest assignment also?

*/

Do you remember how to concatenate two lists or how to merge two objects?

/* old-assignment.js */

/* concatenation */

let a1 = [1,2,4,5];
let a2 = [6,7,8,9];
let result = a1.concat(a2)  /* [1, 2, 4, 5, 6, 7, 8, 9] */


/* merging of two object */

let o1 = { firstName: 'Hawkeye' };
let o2 = { lastName: 'Mihawk' };
let result = Object.assign({}, o1, o2); /* {firstName: 'Hawkeye', lastName: 'Mihawk' } */

Lets see the same with spread operator

/* spread-rest-play.js */
/* Add above arrays and objects here  */

/* lets concat the arrays with spread */

let result = [...a1, ...a2];  /* [1, 2, 4, 5, 6, 7, 8, 9] */

/*
Here we are asking to spread the elements in a1 and a2 to a list.
*/

/* lets try to merge two object */

let result = {...o1, ...o2};

/*
Let your creativity run wild
Try different things with all the things mentioned here.
Let me know if you find anything new.
*/

One of the use case that I found about spread/rest is below


/* returns the sum of 3 elements */
function f1(a, b, c) {
  return a + b + c;
}

/* if you have parameter for that function in a list */

array = [1,2,3];
let oldMethodResult = f1(array[0], array[1], array[2]);
let newMethodResult = f1(...array);

/* 
both gives the same result
In the new method we are just spreading the array
*/

/* 
If we want result of sum of n numbers we will create another function which takes n arguments
 */


/* returns the sum of all arguments to the array */
function f2(...list) {
  return list.reduce((r, e) => r + e, 0);
}

f2(1,2,3) /* 6 */
f2(1,2,3,4) /* 10 */
f2(1,2,3,4,5) /* 15 */
f2(...array) /* 6 */

/*
There is no need for us to the write function for each no of arguments because we are using rest parameter in this case.
*/

Let me know if you struck somewhere. I'm Happy to help you.

References: