How to destructure into dynamically named variables in ES6?
Asked Answered
H

6

103

Let's suppose I have the following object:

const user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};

And that I want only the id and fullName.

I will do the following :

const { id, fullName } = user

Easy-peasy, right?

Now let's suppose that I want to do the destructuring based on the value of another variable called fields.

const fields = [ 'id', 'fullName' ]

Now my question is : How can I do destructuring based on an array of keys?

I shamelessly tried the following without success:

let {[{...fields}]} = user and let {[...fields]} = user. Is there any way that this could be done?

Thank you

Hagi answered 11/3, 2016 at 11:36 Comment(2)
Here's a related question about destructuring all properties: #31908470 - Probably the same answer applies hereReprobate
If fields were changed to be an empty array then you would be creating no variables and any code after that would be jeopardized. Using a const with literals ensures that risk could be determined beforehand but something like fields = nonliteralvar would create problems.Heronry
I
23

Short answer: it's impossible and it won't be possible.

Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.

Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.

Illustrate answered 30/8, 2016 at 13:14 Comment(3)
That was indeed a code smell. Ended up using another approach.Hagi
The scoping point you raise is unassailable. There is, however, at least one case in which it's not a code smell per se. I.e., when you have a function that accepts a key or keys to be used in iteration. Special versions of omit, pluck & reduce are all cases where dynamic destructuring would aid readability. Dynamic allocation of field names in an object is both possible and an excellent way of enabling DRYer code (i.e. more code sharing). In the same way, dynamic allocation of pointers could help DRY up code without needing to use a hashmap to circumvent a language limitation.Timmy
@Timmy - Attempting to do this in arrOfObjs.reduce((result, {[...destructuringProps]: props, ...rest}) => { /* stuff... */ }, {});, is exactly what led me here. Being able to do this in reduce() somehow would be awesome.Circumvent
C
80

It's not impossible to destructure with a dynamic key. To prevent the problem of creating dynamic variables (as Ginden mentioned) you need to provide aliases.

const user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};

const fields = [ 'id', 'fullName' ];
const object = {};

const {[fields[0]]: id, [fields[1]]: fullName} = user;

console.log(id); // 42
console.log(fullName); // { firstName: "John", lastName: "Doe" }

To get around the problem of having to define static aliases for dynamic values, you can assign to an object's dynamic properties. In this simple example, this is the same as reverting the whole destructuring, though :)

const user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};

const fields = [ 'id', 'fullName' ];
const object = {};

({[fields[0]]: object[fields[0]], [fields[1]]: object[fields[1]]} = user);

console.log(object.id); // 42
console.log(object.fullName); // { firstName: "John", lastName: "Doe" }

sources:

Cortex answered 20/11, 2016 at 0:29 Comment(1)
Unfortunately neither of your proposed solutions answers the question as asked.Soulless
D
34

Paul Kögel's answer is great, but I wanted to give a simpler example for when you need only the value of a dynamic field but don't need to assign it to a dynamic key.

let obj = {x: 3, y: 6};
let dynamicField = 'x';

let {[dynamicField]: value} = obj;

console.log(value);
Denticulate answered 2/3, 2021 at 16:15 Comment(0)
I
23

Short answer: it's impossible and it won't be possible.

Reasoning behind this: it would introduce new dynamically named variables into block scope, effectively being dynamic eval, thus disabling any performance optimization. Dynamic eval that can modify scope in fly was always regarded as extremely dangerous and was removed from ES5 strict mode.

Moreover, it would be a code smell - referencing undefined variables throws ReferenceError, so you would need more boilerplate code to safely handle such dynamic scope.

Illustrate answered 30/8, 2016 at 13:14 Comment(3)
That was indeed a code smell. Ended up using another approach.Hagi
The scoping point you raise is unassailable. There is, however, at least one case in which it's not a code smell per se. I.e., when you have a function that accepts a key or keys to be used in iteration. Special versions of omit, pluck & reduce are all cases where dynamic destructuring would aid readability. Dynamic allocation of field names in an object is both possible and an excellent way of enabling DRYer code (i.e. more code sharing). In the same way, dynamic allocation of pointers could help DRY up code without needing to use a hashmap to circumvent a language limitation.Timmy
@Timmy - Attempting to do this in arrOfObjs.reduce((result, {[...destructuringProps]: props, ...rest}) => { /* stuff... */ }, {});, is exactly what led me here. Being able to do this in reduce() somehow would be awesome.Circumvent
N
16

As discussed before, you can't destruct into dynamically named variables in JavaScript without using eval.

But you can get a subset of the object dynamically, using reduce function as follows:

const destruct = (obj, ...keys) => 
  keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});

const object = { 
  color: 'red',
  size: 'big',
  amount: 10,
};

const subset1 = destruct(object, 'color');
const subset2 = destruct(object, 'color', 'amount', 'size');
console.log(subset1);
console.log(subset2);
Nilgai answered 21/12, 2017 at 2:6 Comment(1)
variation used with thanks in: github.com/davelopware/example-ts-mongo-expressVelours
L
5

You can't destruct without knowing the name of the keys or using an alias for named variables

// you know the name of the keys
const { id, fullName } = user;

// use an alias for named variables
const { [fields[0]]: id, [fields[1]]: fullName } = user; 

A solution is to use Array.reduce() to create an object with the dynamic keys like this:

const user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};

const fields = [ 'id', 'fullName', 'age' ];

const obj = fields.reduce((acc, k) => ({ ...acc, ...(user.hasOwnProperty(k) && { [k]: user[k] }) }), {});

for(let k in obj) {
  console.log(k, obj[k]);
}
Lymphangitis answered 12/9, 2019 at 13:17 Comment(0)
A
2

I believe that the above answers are intellectual and valid because all are given by pro developers. :). But, I have found a small and effective solution to destructure any objects dynamically. you can destructure them in two ways. But both ways are has done the same action. Ex:

const user = { 
  id: 42, 
  displayName: "jdoe",
  fullName: { 
      firstName: "John",
      lastName: "Doe"
  }
};
  1. using "Object.key", "forEach" and "window" object.

    Object.keys(user).forEach(l=>window[l]=user[l]);
    
  2. Simply using Object. assign method.

    Object.assign(window, user)
    

Output:

console.log(id, displayName, fullName)
// 42 jdoe {firstName: "John", lastName: "Doe"}

Anyway, I am a newbie in JS. So, don't take it as an offense if you found any misleading info in my answer.

Apprehension answered 11/3, 2016 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.