16

In its most basic form, having an array of objects:

let arr = [
  {val:"a"},
  {val:"b"}
];

How can destructuring be used, to obtain only the values ['a', 'b'].

getting the first value is easy:

let [{val:res}] = arr; //res contains 'a'

Obtaining all values inside the array can be done with the rest operator:

let [...res] = arr; //res contains all objects

Combining those, I expected to be able to use:

let [...{val:res}] = arr; //undefined, expected all 'val's (['a', 'b'])

The above returns undefined (Tested in FF). Some further testing seems to indicate that adding the rest operator when using an object destructuring as well doesn't use the iteration, but gets back the original object, e.g. let [...{length:res}] = arr; //res= 2. Some other trials, such as let [{val:...res}] = arr; or let [{val}:...res] = arr; produce syntax errors.

It's easy enough to do with other methods, such as using map on the array, but mostly I stumble upon this problem while destructuring multiple levels (an array with objects which have their own property containing an array). Therefore I'm really trying to get around how to do it solely with destructuring. For convenience: a test fiddle

edit

My apologies if I failed to explain the goal of the question. I'm not looking for a solution to a specific problem, only to find the correct syntax to use when destructuring.

Otherwise formulated, a first question would be: in the example above, why doesn't let [...{val:res}] = arr; return all values (['a', 'b']). The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here). It seems that the latter is not supported, but I haven't come across any documentation that (and why) it wouldn't be.

Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • What exactly did you expect `res` to become when destructuring like that? What is "all vals"? – Bergi Oct 16 '16 at 18:03
  • @Bergi I had expected it to return `['a', 'b']` (it being a combination of `let [{val:res}] = arr;` and `let [...res] = arr;` ) – Me.Name Oct 16 '16 at 18:05
  • @Me.Name _"The second question would be: what is the proper syntax to use a rest operator with a nested object destructuring? (pretty sure I've gotten some definitions mixed up here)."_ See [What is SpreadElement in ECMAScript documentation? Is it the same as Spread operator at MDN?](http://stackoverflow.com/questions/37151966/what-is-spreadelement-in-ecmascript-documentation-is-it-the-same-as-spread-oper) – guest271314 Oct 16 '16 at 18:41
  • @Me.Name Is requirement to not use a function call or loop? – guest271314 Oct 23 '16 at 22:52

5 Answers5

10

You can destructure nested objects like this

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Nested_object_and_array_destructuring

let arr = [
  {val:"a"},
  {val:"b"}
];

const [{val: valueOfA}, {val: valueOfB}] = arr

console.log(
  valueOfA, valueOfB
)
synthet1c
  • 6,152
  • 2
  • 24
  • 39
  • 1
    Thanks for your answer. I know `map` can be used, I could also use `for({val:v} of arr) console.log(v);` to combine destructering and looping or even use destructuring in `map`, but if the rest operator can be used on the objects themselves and destructuring can be used on nested properties, why can't the rest operator be used on object destructuring? I can't find documentation on its support, but neither on it not being supported. – Me.Name Oct 16 '16 at 10:41
  • actually I am completely incorrect. You can destructure nested objects – synthet1c Oct 16 '16 at 10:45
  • Each element at a time it is indeed possible, but how would you do the same for an unknown amount of entries? (not counting `for..of` ) – Me.Name Oct 16 '16 at 11:03
  • @Me.Name _"(not counting for..of )"_ Why cannot `for..of` be used? What is requirement? – guest271314 Oct 16 '16 at 11:15
  • `for...of` is an iterator loop, The variables are declared each iteration, unlike a `for...in` or `for` loop – synthet1c Oct 16 '16 at 11:20
  • @guest271314 The goal is to find a generic approach. E.g. to be able to use it on inner arrays such as getting all vals of `[{foo:1, arr:[ {val:"a"}, {val:"b"}]},{foo:2, arr:[ {val:"c"}, {val:"d"}]} ]` – Me.Name Oct 16 '16 at 11:21
  • @Me.Name _"how would you do the same for an unknown amount of entries?"_ see http://stackoverflow.com/questions/39673627/how-to-use-destructuring-assignment-to-define-enumerations-in-es6/ – guest271314 Oct 16 '16 at 11:21
  • @guest271314 Thank you for the link. However as mentioned in my question, I know how to create work arounds, either with `map`, `forEach`, `reduce`, `for..of` or custom iterators. It's not a matter of finding a solution to this specific simplified array, but I can't imagine there isn't a way to do the same with destructuring. I have the feeling I''m missing some sort of syntax – Me.Name Oct 16 '16 at 11:24
  • Use approach at Answer. You would need to write nearly as much at left side of `=` as is at right side of `=` to assign variables given array at previous comment. Though you may stumble upon something simpler if you continue trying to do so, or may find that `for..of` loop achieves the same result. – guest271314 Oct 16 '16 at 11:26
9

Why doesn't let [...{val:res}] = arr; return all values (['a', 'b'])?

You seem to confuse the rest syntax with array comprehensions.

If you assign a value to [someElements, ...someExpression], the value is tested to be iterable and then each element generated by the iterator is assigned to the respective someElements variable. If you use the rest syntax in the destructuring expression, an array is created and the iterator is ran till its end while filling the array with the generated values. Then that array is assigned to the someExpression.

All of these assignment targets can be other destructuring expressions (arbitrarily nested and recursively evaluated), or references to variable or properties.

So if you do let [...{val:res}] = arr, it will create an array and fill that with all the values from the iterator of arr:

let {val:res} = Array.from(arr[Symbol.iterator]())

You can see now why that ends up with undefined, and why using something like [...{length:res}] does yield a result. Another example:

let [{val:res1}, ...{length: res2}] = arr;
console.log(res1) // 'a'
console.log(res2) // 1 (length of `[{val: 'b'}]`)

How can destructuring be used to obtain only the values ['a', 'b']?

Not at all. Use the map method.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • @guest271314 Why? If you refer to your post, what does this have to do with my answer? – Bergi Oct 17 '16 at 17:43
  • _"can destructuring be used to obtain only the values `['a', 'b']`?"_. "Not at all" portion of your Answer. It is possible by declaring a unique variable for each expected value retrieved from input array at right side of `=`, then pushing defined variables to an array. Tried to use `spread` element to expand the array within the destructuring assignment using `{[prop]: [...res]}`, though have not yet found a way to expand the `"a"` to `res` at next index of assignment – guest271314 Oct 17 '16 at 17:49
  • @guest271314 If you declare multiple variables, it does not work for arbitrary-length arrays. If you use a loop, then that's not destructuring any more. – Bergi Oct 17 '16 at 17:57
  • Did not consider an arbitrary-length array, as the array at OP contains only four elements. For an arbitrary-length array, first impression, here, would be to try to assign the variable as a property of an object, instead of for example `a`; not sure if that is possible. Will try that approach. The reason posted comment here was due to "Not at all" portion of your Answer. If you are ok with that portion of your Answer, am here as well. Also commented, as you might be able to notice a possible improvement to that portion of the Answer – guest271314 Oct 17 '16 at 18:02
  • See what you mean `...` – guest271314 Oct 18 '16 at 00:21
1

Beside mapping with a callback for the value

let arr = [{ val: "a" }, { val: "b" }];

console.log(arr.map(o => o.val));

you could use deconstructiong inside of the paramter list and use only the value to return.

let arr = [{ val: "a" }, { val: "b" }];

console.log(arr.map(({val}) => val));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Hi Nina. Thanks for answering my post. I always love your posts and the concise answers you provide :) However in this case it's not about the specific scenario, but rather to find a generic syntax solution. I thought there would be a syntax to do the same with destructering for an unknown number of elements so the same could be used for example on `[{foo:1, arr:[ {val:"a"}, {val:"b"}]},{foo:2, arr:[ {val:"c"}, {val:"d"}]} ]` – Me.Name Oct 16 '16 at 11:31
  • 1
    my misandertanding is the result, you want to reach. i rely on examples (very much) and for me, it is not clear, what you like to achieve. i can not see, if the object is the source or the target object. maybe itz would be more clear, if you include the source and target. – Nina Scholz Oct 16 '16 at 15:07
  • The goal is to find out how (and if) destructuring can be used to get multiple values such as this. Since the `rest` operator exists ( `let [...res] = arr;` is valid) and nested properties are allowed, why doesn't `let [...{val:res}] = arr;` give all values. I was assuming that it was some sort of oversight on my part, but I'm beginning to fear it's not possible. It's not about a specific problem or example, so I'm afraid I can't give a specific source and targets, only examples. – Me.Name Oct 16 '16 at 17:41
1

At this point of time you can use both For of loop with ES6 Object destructuring.

let arr = [{val:"a"},{val:"b"}];
    
    
    for (const item in arr){
        const {val} = arr[item];
        console.log(val);
    }
Roman Olly
  • 21
  • 1
0

You can declare assignment target before destructuring assignment; at destructuring target, set values of assignments target indexes by from destructuring source

let arr1 = [{val: "a"}, {val: "b"}];

let arr2 = [{"foo":1,"arr":[{"val":"a"},{"val":"b"}]}
           , {"foo":2,"arr":[{"val":"c"},{"val":"d"}]}];

let [res1, res2] = [[], []];

[{val: res1[0]}, {val: res1[1]}] = arr1;

[{arr: [{val:res2[0]}, {val:res2[1]}]}
, {arr: [{val:res2[2]}, {val:res2[3]}]}] = arr2;

console.log(res1, res2);

You can alternatively use rest element at target to collect values at source by including comma operator following object pattern to return value pulled from object

let arr = [{val: "a"}, {val: "b"}];

let [...res] = [({val} = arr[0], val), ({val} = arr[1], val)];

console.log(res)
guest271314
  • 1
  • 15
  • 104
  • 177