Object and Array Destructuring in JavaScript

9 min read.

In this video you’ll learn how to more easily extract data from both Arrays and Objects in JavaScript using ES6’s (ES2015) destructuring feature.

Video

Post

In this post, we’ll cover an ES2015 feature called destructuring. To better understand it, let’s take a look at some of the basics of Javascript objects. To add a single property to an object you use dot notation. With dot notation, you can only add properties to an object one at a time. The same syntax can be used to extract data, again, one property at a time.

var user = {};
user.name = 'Tyler McGinnis';
user.handle = '@tylermcginnis33';
user.location = 'Eden, Utah';

var name = user.name;
var handle = user.handle;

If you wanted to add multiple properties to an object at the same time, you would need to use Javascript’s “object literal notation” when you initialize the object.

var user = {
  name = 'Tyler McGinnis';
  handle = '@tylermcginnis33';
  location = 'Eden, Utah';
};

var name = user.name;
var handle = user.handle;

There’s a way to add properties one at a time, extract properties one at a time, add multiple properties at the same time, but unfortunately, there’s no comparable way to extract multiple properties from an object at the same time. That is, until “destructuring” in ES2015. Destructuring allows us to extract multiple properties from an object. This can drastically decrease the amount of code we need to write when we want to extract data from an object, because what used to look like this,

var name = user.name;
var handle = user.handle;
var location = user.location;

can now look like this,

var { name, handle, location } = user;

The syntax can be a little bit weird but know that these two blocks of code are identical in that they both create and initialize three new variables. You can think of it like this, if you want to add properties to an object, do it as you are used to, on the right-hand side of the equal sign. If you want to extract properties from an object, do it on the left-hand side of the equal sign.

Destructuring also allows you to destructure the results of function invocations. For example, below we have a function called getUser() which returns the user object. Rather than invoking getUser() and grabbing all of the properties off of it one by one, we could get the same result by destructuring the result of that invocation.

function getUser () {
  return {
    name: 'Tyler McGinnis',
    handle: '@tylermcginnis33',
    location: 'Eden, Utah'
  };
}

var { name, handle, location } = getUser();

Up until this point we’ve talked about how destructuring helps us extract data from objects, but what about arrays? Though not as common as object destructuring, array destructuring is a thing and it is still pretty useful in certain circumstances, specifically when the location of an item in the array is the main differentiator for that item. So here we have a user array with each item being a unique piece of information about the user,

var user = ['Tyler McGinnis', '@tylermcginnis33', 'Eden, Utah'];

You’ll notice that this array probably should just be an object. But sometimes you have to take what you can get from weird external API’s. Typically if we want to better identify each item in the array we need to create a variable for each item.

var name = user[0];
var handle = user[1];
var location = user[2];

However just like with objects, array destructuring allows us to more effectively extract items from an array so the above code, can now look like the code below.

var [ name, handle, location ] = user;

Just as we saw from objects you can use array destructuring with function invocations. For example, below “split” is going to return an array with each item in the array being a specific property of the car.

var cvs = '1997,Ford,F350,MustSell!'
var [ year, make, model, description ] = csv.split(',');

By using array destructuring we are able to extract each property into their own user readable variable.

So that’s it in regards to the basics of destructuring, again destructuring allows us to easily extract data from an object or an array. There are however what I’d consider to be more advanced features of destructuring that are worth taking a look at.

For example, what if when we do destructure an object, we wanted the variable name to be different than the property name on that object. So say we had an object that looked like this

var user = {
  n: 'Tyler McGinnis',
  h: '@tylermcginnis33',
  l: 'Eden, Utah'
};

since we are not masochists and we actually like the other developers on our team, we don’t want to make three one letter variable names. Instead what we can do is have the property names on the left of the colon and the new variable names on the right, now we are not only destructuring the user object, but we are also renaming the poorly named properties into more easily understood variable names.

var { n: name, h: handle, l: location } = user;
console.log(name) // Tyler McGinnis
console.log(handle) // @tylermcginnis33
console.log(location) // Eden, Utah

This may seem like a rarely used feature, but it is actually pretty common. To find a real world example we don’t really have to look very far. This is the implementation of the render method in React Router Native’s Link component. Note how we’re renaming component with a lower case “c” to Component with a capitalized “c”.

render () {
  const { component: Component, to , replace, ...rest } = this.props
  return <Component {...rest} onPress={this.handlePress}/>
}

Next lets talk about function arguments and parameters. Below we have a fetchRepos() function which is going to be in charge of fetching a group of repositories from the github API.

function fetchRepos (language, minStars, maxStars, createdBefore, createAfter) {

}

The first thing you’ll notice is that we have a lot of control over the type of repositories that we will be fetching. Fortunately, this leads to a stupid amount of arguments that can be passed into the function. Now when we invoke our fetchRepos() function we have two issues. First we need to remember or look up which arguments go in which order, second we need to read and hope that the documentation has instructions for what to do with our arguments that we do not care about. In this case, we will just use “null” and hope for the best.

function fetchRepos (language, minStars, maxStars, createdBefore, createAfter) {

}

fetchRepos('JavaScript', 100, null, new Date('01.01.2017').getTime(),null);

The good news is that destructuring helps us with both of these problems. First let’s solve the positional parameters problem, what if instead of passing in each argument one by one, we pass in an object instead. Now before we ever need to look at the function definition of fetchRepos() we know exactly what information it needs. And even more important, order no longer matters.

function fetchRepos (language, minStars, maxStars, createdBefore, createAfter) {

}

fetchRepos({
  language: 'JavaScript',
  maxStars: null,
  createdAfter: null,
  createdBefore: new Date('01/01/2017').getTime(),
  minStars: 100,
});

Now we need to modify the fetchRepos() function definition. This is where destructuring comes into play. Because we are receiving an object as the argument to the function, we can destructure it. So now the code above, can be changed to this.

function fetchRepos ({ language, minStars, maxStars, createdBefore, createAfter }) {

}

fetchRepos({
  language: 'JavaScript',
  maxStars: null,
  createdAfter: null,
  createdBefore: new Date('01/01/2017').getTime(),
  minStars: 100,
});

Again the biggest benefit here is that we have removed the order out of the equation entirely, so that’s one less thing we have to worry about.

The second problem we had earlier with our code was that we needed to figure out what to do with the arguments we did not care about. Before we just passed in “null” but now that we are passing in an object rather than arguments one by one, we can actually just remove the “null” values altogether and that will give us a function invocation that looks just like this.

function fetchRepos ({ language, minStars, maxStars, createdBefore, createAfter }) {

}

fetchRepos({
  language: 'JavaScript',
  createdBefore: new Date('01/01/2017').getTime(),
  minStars: 100,
});

This now leads us back to our function definition of fetchRepos(). We need a way to establish default values for any properties that aren’t on the arguments object when the function is invoked. Typically that would look like this.

function fetchRepos ({ language, minStars, maxStars, createdBefore, createAfter }) {
  language = language || All;
  minStars = minStars || 0;
  maxStars = maxStars || '';
  createdBefore = createdBefore || '';
  createdAfter = createdAfter || '';
}

fetchRepos({
  language: 'JavaScript',
  createdBefore: new Date('01/01/2017').getTime(),
  minStars: 100,
});

For each different possible property we’d set the value of that property to itself or a default value if the original value was undefined. Luckily for us, another feature of destructuring is it allows you to set default values for any properties. If a partially destructured value is “undefined” it will default to whatever you specify. What that means is that the ugly code above can be transformed into this,

function fetchRepos({ language='All', minStars=0, maxStars='', createdBefore='', createdAfter='' }){

}

Where we set the default value of each property in the same place where we just destructured the parameters. Now that we’ve seen the power of using object destructuring to destructure an object’s parameters, can the same thing be done with array destructuring? Turns out it can.

My favorite example of this is with Promise.all, below we have a getUserData() function.

function getUserData (player) {
  return Promise.all([
    getProfile(player),
    getRepos(player)
  ]).then(function (data) {
    var profile = data[0];
    var repos = data[1];

    return {
      profile: profile,
      repos: repos
    }
  })
}

Notice it’s taking in a player and returning us the invocation of calling Promise.all. Both “getProfile” and “getRepos” return us a promise. The whole point of this getUserDate() function is that it is going to take in a player and it is going to return us an object with that player’s profile as well as that players repositories. If you are not familiar with the Promise.all bascially what is going to happen here is “getProfile” and “getRepos” are both asynchronous functions. So when those promises resolve or when we get that information back from the github API, the function that we passed to “.then” is going to be invoked and it is going to be passed as an array (in this case we are calling it data), the first item in that array is going to be the user’s profile and the second item in the array is going to be the user’s repositories. So you’ll notice that order matters here. Ff we were to pass another invocation into our Promise.all invocation, say getUsersFollowers(), then the third item in our data array would be their followers.

The first update we can make to this code is we can go ahead and destructure our data array. Now we still have our profile variable and we still have our repos variable but instead of plucking out the items one by one we just do it with destructuring.

function getUserData (player) {
  return Promise.all([
    getProfile(player),
    getRepos(player)
  ]).then(function (data) {
    var [ profile, repos ] = data;
    return {
      profile: profile,
      repos: repos
    }
  })
}

Now just as we saw with objects, we can move that destructuring into the parameter itself.

function getUserData (player) {
  return Promise.all([
    getProfile(player),
    getRepos(player)
  ]).then(function ([ profile, repos ]) {
    return {
      profile: profile,
      repos: repos
    }
  })
}

We still have the profile variable and we still have the repos variable but those are being created with array destructuring inside of the function’s parameters.

Liked this post? Share it 🕺

Newsletter

Listen, I get it, newsletters are the worst.

But, as Louis from Colorado says, "This newsletter sucks less than the others".

We only send spam. That's a joke.