Destructure object properties inside array for all elements - javascript

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.

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.

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
)

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));

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);
}

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)

Related

Why do i get just a number from console.log(Array.push()); in Javascript? [duplicate]

Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
I don't know if this has already been proposed or asked before; Google searches returned only a myriad number of questions related to the current functionality of Array.push().
Here's an example implementation of this functionality, feel free to correct it:
;(function() {
var _push = Array.prototype.push;
Array.prototype.push = function() {
return this[_push.apply(this, arguments) - 1];
}
}());
You would then be able to do something like this:
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray.push({}));
Where someFunction modifies the object passed in as the second parameter, for example. Now the contents of someArray are [{"someKey": "hello world"}].
Are there any drawbacks to this approach?
See my detailed answer here
TLDR;
You can get the return value of the mutated array, when you instead add an element using array.concat[].
concat is a way of "adding" or "joining" two arrays together. The awesome thing about this method, is that it has a return value of the resultant array, so it can be chained.
newArray = oldArray.concat[newItem];
This also allows you to chain functions together
updatedArray = oldArray.filter((item) => {
item.id !== updatedItem.id).concat[updatedItem]};
Where item = {id: someID, value: someUpdatedValue}
The main thing to notice is, that you need to pass an array to concat.
So make sure that you put your value to be "pushed" inside a couple of square brackets, and you're good to go.
This will give you the functionality you expected from push()
You can use the + operator to "add" two arrays together, or by passing the arrays to join as parameters to concat().
let arrayAB = arrayA + arrayB;
let arrayCD = concat(arrayC, arrayD);
Note that by using the concat method, you can take advantage of "chaining" commands before and after concat.
Are there any substantial reasons why modifying Array.push() to return the object pushed rather than the length of the new array might be a bad idea?
Of course there is one: Other code will expect Array::push to behave as defined in the specification, i.e. to return the new length. And other developers will find your code incomprehensible if you did redefine builtin functions to behave unexpectedly.
At least choose a different name for the method.
You would then be able to do something like this: someFunction(value, someArray.push({}));
Uh, what? Yeah, my second point already strikes :-)
However, even if you didn't use push this does not get across what you want to do. The composition that you should express is "add an object which consist of a key and a value to an array". With a more functional style, let someFunction return this object, and you can write
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
return obj;
}
someArray.push(someFunction(value, {}));
Just as a historical note -- There was an older version of JavaScript -- JavaScript version 1.2 -- that handled a number of array functions quite differently.
In particular to this question, Array.push did return the item, not the length of the array.
That said, 1.2 has been not been used for decades now -- but some very old references might still refer to this behavior.
http://web.archive.org/web/20010408055419/developer.netscape.com/docs/manuals/communicator/jsguide/js1_2.htm
By the coming of ES6, it is recommended to extend array class in the proper way , then , override push method :
class XArray extends Array {
push() {
super.push(...arguments);
return (arguments.length === 1) ? arguments[0] : arguments;
}
}
//---- Application
let list = [1, 3, 7,5];
list = new XArray(...list);
console.log(
'Push one item : ',list.push(4)
);
console.log(
'Push multi-items :', list.push(-9, 2)
);
console.log(
'Check length :' , list.length
)
Method push() returns the last element added, which makes it very inconvenient when creating short functions/reducers. Also, push() - is a rather archaic stuff in JS. On ahother hand we have spread operator [...] which is faster and does what you needs: it exactly returns an array.
// to concat arrays
const a = [1,2,3];
const b = [...a, 4, 5];
console.log(b) // [1, 2, 3, 4, 5];
// to concat and get a length
const arrA = [1,2,3,4,5];
const arrB = [6,7,8];
console.log([0, ...arrA, ...arrB, 9].length); // 10
// to reduce
const arr = ["red", "green", "blue"];
const liArr = arr.reduce( (acc,cur) => [...acc, `<li style='color:${cur}'>${cur}</li>`],[]);
console.log(liArr);
//[ "<li style='color:red'>red</li>",
//"<li style='color:green'>green</li>",
//"<li style='color:blue'>blue</li>" ]
var arr = [];
var element = Math.random();
assert(element === arr[arr.push(element)-1]);
How about doing someArray[someArray.length]={} instead of someArray.push({})? The value of an assignment is the value being assigned.
var someArray = [],
value = "hello world";
function someFunction(value, obj) {
obj["someKey"] = value;
}
someFunction(value, someArray[someArray.length]={});
console.log(someArray)

Check if an array of arrays contains a value in javascript

I know that if there is an array of values it must be used this approach:
console.log(['joe', 'jane', 'mary'].includes('jane')); // true
But in case of an array of arrays, is there a short way to do it? Without other computations between.
For this input:
[['jane'],['joe'],['mary']]
You can use flat method to flatten the array. For more neted array, you can also mention depth like flat(depth)
let arr = [["jane"],["joe"],["mary"]];
arr.flat().includes('jane'); //true
You can easily achieve this result using some
arr.some((a) => a.includes("jane"))
const arr = [
["jane"],
["joe"],
["mary"]
];
const arr2 = [
["joe"],
["mary"]
];
console.log(arr.some((a) => a.includes("jane")));
console.log(arr2.some((a) => a.includes("jane")));
it can also be done by first flattening the 2d arrays in 1 d aaray and then using includes to find whether the array contains the element or not
var arr = [['jane'],['joe'],['marry']]
var newarr=[].concat(...arr)
var v=newarr.includes('jane')
console.log(v)

How to destructure an array of objects? [duplicate]

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.
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.
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
)
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));
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);
}
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)

Non-destructively reversing an array in Javascript

Say I have a function:
function linesReverser(lines) {
var localLines = lines.slice();
localLines[1].reverse();
return _.flatten(localLines);
}
And using it like so:
var input = [["Hello"],["Hello", "World"]["Attention", "Please"]];
var output1 = linesReverser(input); //["Hello", "World", "Hello", "Attention", "Please"]
var output2 = linesReverser(input); //["Hello", "Hello", "World", "Attention", "Please"]
Notice how the object reference is being shared. I am new to JS, but I thought copying the values would alleviate this issue (line.slice()), but it doesn't seem to work. Is this because of the nested arrays?
How can I non-destructively/immutably perform a reverse?
You can duplicate the array using ES6's spread operator, then destructively reverse the duplicate:
const arr1 = [1,2,3];
const arr2 = [...arr1].reverse();
// arr1 => [1,2,3]
// arr2 => [3,2,1]
You're making a shallow copy of the lines array. To copy the nested arrays, you need to slice each one.
var localLines = lines.map(function(arr) {
return arr.slice();
});
The .map method will return a new Array of the return values, which are a slice of each nested Array.
FWIW, here's a shorter version that will work in modern browsers, though I'd probably stick with the first one.
var localLines = lines.map(Array.apply.bind(Array, null));
A non-destructive approach to reverse an array in Javascript
ES6
var array = [1,2,3];
array.reduce((ary, ele) => {ary.unshift(ele); return ary}, []);
// => [3,2,1];
// array => [1,2,3];
ES5
array.reduce(function(obj, ele){
obj.unshift(ele);
return obj;
},[]);
You can safely (deep) copy a nested array with JSON.parse and JSON.stringify, therefore the reference is not shared. Then destructively reverse the duplicate:
let array1 = [[1, 3], [50, 1]];
let arrayCopy = JSON.parse(JSON.stringify(array1));
arrayCopy.reverse(); //[[50,1],[1,3]]
checkout cloning of arrays for more info

How to access multiple properties of a javascript object?

If I have the array:
my_array = [{a:3,b:4,c:7},{a:5,b:8,c:6}]
property_names = ["a","c"]
How can use the property_names array against my_array to get the following output?:
subset_array = [[3,7],[5,6]]
var my_array = [{a:3,b:4,c:7}, {a:5,b:8,c:6}];
var keys = ['a', 'c'];
my_array.map(function(d){
return keys.map(function(k) {
return d[k];
});
});
This will give you [[3, 7], [5, 6]] and seems to be just what you want.
[Obsolete initial answer; before the OP updated his question]
Use o.a and o.c - no magic involved and no kittens harmed!
You could also use the [] syntax: o['a'] and o['c'] - but usually you only use it if you want to access a dynamic property name (i.e. a variable instead of a quotes string inside the [])
If you want an array with both values:
var a_and_c = [o.a, o.c];
Now you can use a_and_c[0] and [1] to access those two values.
You can access the properties by two notations:
Either o.a and o.c or o['a'] and o['c'].
EDIT
If you want to create a new array containing both values, then just do so using, e.g, the code suggested by #Reid :
[o.a, o.c]
2nd EDIT
So just build your array up inside the map function like the following:
var filter = [ "a","c","f","zzz"]; // should
subset_array = my_array.map( function(v){
var newArr = [];
for( var i=0; i<filter.length; ++i ) {
if ( typeof v[ filter[i] ] !== 'undefined' ) {
newArr.push( v[ filter[i] ] );
}
}
return newArr;
} );
One thing to note is, however, that as long as you use primitive types (like the int values in the example) the above code will clone the properties. This means, if you change a value in my_array at some point, subset_array will not be changed implicitly, but you have to recreate subset_array or adjust the value there, too.
This is not a full answer, however I believe it will be of use:
var values = []
var obj = {a: 1, b: 2, c: 3}
for (var prop in obj) {
if (obj.hasOwnValue(prop)) { // skip props in [[prototype]], no effect here
values.push(obj[prop])
}
}
values // -> [2, 1, 3] (in SOME UNDEFINED ORDER)
Key points:
Iterate [all] object property names for the object (but not [[prototype]] chain)
Access value dynamically from property name
Properties are not ordered; sort values if needed, for instance
This can be expanded to add different "exclusion" rules, for instance. It can also be adapted to different higher-order functions.
Happy coding.

Categories