95

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

Anas
  • 1,761
  • 1
  • 13
  • 22
  • 1
    Here's a related question about destructuring all properties: http://stackoverflow.com/questions/31907970/how-do-i-destructure-all-properties-into-the-current-scope-closure-in-es2015 - Probably the same answer applies here – CodingIntrigue Mar 11 '16 at 12:07
  • 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. – Michael Theriot Mar 17 '16 at 19:35

6 Answers6

76

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:

Paul Kögel
  • 1,801
  • 1
  • 13
  • 6
  • 15
    Unfortunately neither of your proposed solutions answers the question as asked. –  Nov 20 '16 at 02:40
28

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);
junvar
  • 11,151
  • 2
  • 30
  • 46
22

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.

Ginden
  • 5,149
  • 34
  • 68
  • That was indeed a code smell. Ended up using another approach. – Anas Sep 05 '16 at 15:23
  • 6
    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. – james_womack Jan 28 '17 at 00:48
  • 1
    @james_womack - 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. – Jack_Hu Jul 04 '22 at 17:52
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);
Muhammet Enginar
  • 508
  • 6
  • 14
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]);
}
Fraction
  • 11,668
  • 5
  • 28
  • 48
0

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.