Some use cases of object destructuring
Destructuring introduced in ES6 (ES2015) is a nice way of extracting values and properties from arrays and objects and, at the same time, saving them into variables.
How does it work?
Object destructuring is a cool way of replacing the dot notation when referring to the properties of objects. Assume we have the following very simple object:
const book = {
title: 'Harry Potter and the Philosopher\'s Stone',
author: 'J. K. Rowling',
review() {
return 'A great book!';
}
};
In the pre-ES6 era we needed to access the title
property like this:
const title = book.title;
console.log(title); // Harry Potter and the Philosopher\'s Stone
With destructuring, we can write the following syntax:
const { title } = book;
console.log(title); // Harry Potter and the Philosopher\'s Stone
The { title }
notation indicates that we are extracting a property of an object (curly braces). title
is the name of the property in book
we want to pull out and we save it into the variable called title
.
So far the code is not much shorter but we can use the same const
declaration to extract other properties from the same object:
const { title, review } = book;
console.log(title); // Harry Potter and the Philosopher's Stone
console.log(review()); // A great book!
We could now save a line of code and the shorter the code is, the better.
It’s also possible to give another, more expressive name to the extracted variables:
const {
title: bookTitle,
review: bookReview
} = book;
console.log(bookTitle); // Harry Potter and the Philosopher's Stone
console.log(bookReview()); // A great book!
In this case we save the title
and review
properties of book
into the bookTitle
and bookReview
variables, respectively. The title
and review
variables won’t be available anymore (because they are not declared) and trying to accessing them will throw a ReferenceError
.
Use cases
Now let’s have a look at some cool use cases of object destructuring.
Requiring methods
This is my all-time favourite. We very often need to require modules at the top of the files in Node.js. Every time we need a method from a module (and not the whole module itself), we can use destructuring.
The expect
method of the popular test assertion library, Chai is a great example:
const { expect } = require('chai');
Here we pull out the expect
method and save it into a variable of the same name. Nothing can stop us now from writing a cool assertion:
expect(author).to.equal('J. K. Rowling');
We can also use destructuring when we import our custom modules. Say that we have a file called maths.js
and export several methods:
module.exports.sum = (a, b) => a + b;
module.exports.product = (a, b) => a * b;
module.exports.power = (a, b) => a ** b;
module.exports
is an object and we are adding three methods to it, sum
, product
and power
. As such, we can use destructuring when requiring them in another file:
const { product, power } = require('./maths');
console.log(product(3, 5)); // 15
console.log(power(2, 4)); // 16
Cool!
Use of destructuring in array methods
JavaScript’s map
and filter
methods can be applied on arrays and we can use destructuring to make our code a bit cleaner when using these methods too.
Assume we have a books
object with the following properties:
const books = [
{
title: 'Harry Potter and the Philosopher\'s Stone',
author: 'J. K. Rowling',
year: 1997,
reviews: {
excellent: 3,
good: 5,
fair: 2
}
},
{
title: 'The Lord of the Rings',
author: 'J. R. R. Tolkien',
year: 1954,
reviews: {
excellent: 6,
good: 3,
fair: 1
}
},
{
title: 'The Girl on the Train',
author: 'Paula Hawkins',
year: 2015,
reviews: {
excellent: 2,
good: 4,
fair: 2
}
},
{
title: 'The Bourne Identity',
author: 'Robert Ludlum',
year: 1980,
reviews: {
excellent: 6,
good: 1,
fair: 2
}
}
];
No surprise here, Harry Potter is still alive and rules.
Because we love reading we can map over the books and create a custom review message by adding the “… is great!” string to the title of each book.
Without destructuring we have to do something like this:
const booksRead = books.map((book) => {
return `${ book.title } by ${ book.author } is great!`;
});
Logging booksRead
to the console will return the following:
[ 'Harry Potter and the Philosopher\'s Stone by J. K. Rowling is great!',
'The Lord of the Rings by J. R. R. Tolkien is great!',
'The Girl on the Train by Paula Hawkins is great!',
'The Bourne Identity by Robert Ludlum is great!' ]
As we can see map
accepts a function whose first (and in this case, the only) argument is the current book object in the iteration. Because it’s an object and we will work with its properties in the return
statement, we can apply destructuring.
Let’s pull out the title
and author
properties from books
:
const booksRead = books.map(({ title, author }) => {
return `${ title } from ${ author } is great!`;
});
{ title, author }
indicates that we are extracting the title
and author
properties from an object (curly braces) and saving them into the variables title
and author
, respectively.
Getting one level deeper
OK, this is all good but so far it has only been rather nice then convincing.
Let’s have a look at books
, which also contains a nested object. Can we use destructuring to obtain properties from reviews
?
Yes, we can and the real power of destructuring will show off here.
Our task is to select the books which received an excellent
rating from more than five readers and return the “… is excellent!” text along with the title
and author
of the excellent books. To achieve this we first filter
the books
object and return the books that meet the requirement of being voted excellent by more than five people. Then we map
over the already filtered array and insert the title
and author
into our message.
It looks like this without destructuring:
const excellentBooks = books
.filter((book) => {
return book.reviews.excellent > 5;
}).map((excellentBook) => {
return `${ excellentBook.title } by ${ excellentBook.author } is excellent!`
});
This returns
[ 'The Lord of the Rings by J. R. R. Tolkien is excellent!',
'The Bourne Identity by Robert Ludlum is excellent!' ]
Indeed, these books are excellent.
However, it’s quite inconvenient to use the dot notation for the nested reviews
object to get the excellent
property.
On the other hand, in the chained map
method we only receive the books that are already rated excellent
(we receive it as the returned value from filter
), so the naming of the current book object should reflect this (excellentBook
).
It would be very easy to get lost if we attached another method like sort
to order them by year
, for example. I reckon with the map
part attached the code is already not easy to read.
We can make our code cleaner with destructuring:
const excellentBooks = books
.filter(({ reviews: { excellent } }) => {
return excellent > 5;
}).map(({ title, author }) => {
return `${ title } by ${ author } is excellent!`
});
In the filter
method we only use the excellent
property of books
. Watch how the nested object is shown in the argument of map
. { reviews: { excellent } }
indicates that we extract the reviews
property first, which is also an object, and then we will pull out the excellent
property from it.
The map
part is also easy to read and we don’t have to bother with the naming here. We know that the properties derive from books
and we just pull put the ones we need (title
and author
) to work with.
Conclusion
Object destructuring is a really cool feature and it has been with us for a while. It makes our code cleaner and more readable. It might take a while for one to get used to it but it’s worth the effort because I’m sure the code will be cleaner and the developer will be happier.
Thanks for reading and see you next time.