let { [key]: id = 0, ...rest } = obj - Destructuring to the limit
Tags: javascript, ecmascript, destructuring, objects
Published: 2019-03-21 23:07:01

I recently ran into a problem where I needed the following piece of JavaScript

```javascript
let { [key]: id, ...rest } = obj
```

So in this post I want to explain what this is doing and how it works.

> Spoiler alert: I ended up not implementing it this way because of its obscurity, but it is interesting nontheless

## How I ran into this problem?

Imagine we have the following array
```javascript
const users = [ 
    { name: 'Michael', group: 1 },
    { name: 'Lukas', group: 1 },
    { name: 'Travis', group: 2 },
]
```

and we want to group it by the key `group` and turn it into a hashMap which would look like this

```javascript
{
    '1': [
        { name: 'Michael' },
        { name: 'Lukas' },
    ],
    '2': [
        { name: 'Travis' },
    ]
}
```

Notice how we remove the group from the `user` object.

We can achieve this using

```javascript
users.reduce((result, user) => {
  const { group, ...userData } = user
  result[group] = result[group] || []
  result[group].push(userData)

  return result
}, {})
```

If you are unfamiliar with `reduce` check out my article on [array methods](https://dev.to/mzanggl/array-methods-and-iterables---stepping-up-your-javascript-game-15bo).

My end goal was to make this function dynamic, right now the `group` key is all hardcoded and not computed. But before we look at that, let's check out `const { group, ...userData } = user` since it is exactly the expression I want to talk about, just not dynamic.

## Destructuring

We know that each user has the keys `group` and `name`, so in ES6 we can use a feature called destructuring to get individual values from an object.

For example 
```javascript
const { group } = user
```

would be the same as writing

```javascript
const group = user.group
```

and

```javascript
const { group, name } = user
```

would likewise be the same as

```javascript
const group = user.group
const name = user.name
```

### Rest

Now there is one more complexity in our initial line: `const { group, ...userData } = user`.

`...userData` is taking all key value pairs except for `group` and shallow copies them into a new constant named `userData`. In this case the variable `userData` would be an object with only the `name` property.

Don't confuse the rest parameter with spreading. Spreading would be kind of the opposite.

```javascript
const location = { country: 'Japan', city: 'Tokyo' }

const newLocation = { ...location, zipcode: 123456 }

```

This takes the location object and spreads it out, so `newLocation` will be a completely new object that has all the properties from `location` as well as `zipcode`.

When is something `rest` and when is something `spread`? It all depends on which side the assignment is. If something is on the left side of the assignment it would be `rest`, if something is on the right side of the assignment it would be `spread`.

You can also use the rest parameter for functions.

```javascript
class BaseArray extends Array {
    constructor(...values) { // rest

        super(...values) // spread

    }
}
```

---

With that out of the way, let's look at the dynamic solution.

```javascript
function groupBy(array, key) {
    return array.reduce((result, item) => {
        const { [key]: id, ...rest } = item
        result[id] = result[id] || new []
        
        result[id].push(rest);

        return result;
    }, {})
}
```

Now what the heck is `const { [key]: id, ...rest } = item`?

We already know what `...rest` means, so we can ignore that for now. Before explaining `[key]: id`, let's look at a simpler example.

### Assigning new variable names

Remember this?

```javascript
const user = { group: 1 }
const { group } = user
console.log(group) //? 1
```

What if we wanted to apply the value of `group` to a different variable name? We can do it like this

```javascript
const user = { group: 1 }
const { group: id } = user
console.log(id) //? 1
```

This takes the value of `group` and puts it inside the variable `id`.

This is actually really useful because sometimes keys would be invalid as variable names.

```javascript
const foo = { 'fizz-buzz': true }
const { 'fizz-buzz': fizzBuzz } = foo
```

Now how do we remember this syntax? It's actually quite simple. You just have to think about the side of the assignment again.
When we create objects we have exactly the same syntax

```javascript
const id = 1
const user = {
    group: id
}
```

So if the object is on the right side of the assignment, we give the object `user` a property `group` that holds the variable `id`.

If it is on the left side of the assignment it would be the opposite. 

```javascript
const { group: id } = user
```

We take the value of property `group` and put it inside the variable `id`.

### Finally, computed object properties names

So the only thing left to explain is `[key]`. 

We can use this to access a computed property name, in our case the variable `key` holds the value `group`. 

Again, nothing new here.

How do you add computed keys when creating objects?

Using the same syntax, just that it's on the right side of the assignment!

```javascript
const key = 'group'
const id = 1

const user = {
    [key]: id
}
```

But if we would just write `let { [key] } = obj` under what name are we supposed to access this variable then? Well, we can't, so like with `fizz-buzz` we need to assign it to a new variable using `:`. This combination ultimately creates `[key]: id`.


So that's that, how can we make it even more obscure? By applying a default value to the id!

Usually it would look like this

```javascript
const user = { group: 1 }

const { group = 0, createdAt = null} = user
```

Using a computed property it becomes

```javascript
let { [key]: id = 0, ...rest } = obj
```

References
- [Destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
- [Assigning to new variable names](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Assigning_to_new_variable_names)
- [Computed object property names](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Computed_object_property_names_and_destructuring)