Related
After reading the MDN doc with examples of Destructuring assignment, and there is an example like:
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
and it says: This allows you to simultaneously unpack the properties and indices of arrays.
I took a second thought, isn't it will be fascinating if I could get the length of the array by doing this assignment at the same time getting the indices values out, so I tried this in my console:
const arr = [1, 2]
const [x, y, ...{length}] = arr
console.log(x) // 1
console.log(y) // 2
console.log(length) // 0 | ??? isn't it should give me 2?
// how about this...
const arr = [1, 2]
const {length} = arr
console.log(length) // 2 | ok well, it did work this way, but why not the destructuring assignment?
So, does anybody know why the length did not got assigned with value 2 in the first destructuring assignment? or did I found a bug in JavaScript?
What this does
[a, b, ...something] = someArray
is it takes the rest of the elements (index 2 and after) from someArray and puts them into a single variable as an array.
Doing
...{ pop, push }
takes two properties from that array: Array.prototype.pop and Array.prototype.push. But there aren't any more elements, because the original array had only 2 items, so doing
...{ length }
will give you 0.
If the original array did have more items, you'd see a length. For example:
const arr = [1, 2, 3, 4, 5]
const [x, y, ...{length}] = arr;
console.log(length);
The first two items are put into x and y. The last three items are taken as an array and then the length property is destructured from that array - so the length is 3.
Either add the number of destructured items previously to the length to get the actual length
const arr = [1, 2]
const [x, y, ...{length}] = arr
const actualLength = length + 2; // because 2 items were destructured; x and y
console.log(x) // 1
console.log(y) // 2
console.log(actualLength);
Or use object destructuring syntax instead
const arr = [1, 2]
const { 0: x, 1: y, length } = arr;
console.log(x) // 1
console.log(y) // 2
console.log(length);
Or, more understandable at a glance than either of the above - just do const { length } = arr on a separate line.
Let's see what happens when you only take one element off
const arr = [1, 2]
const [x, ...{length}] = arr
console.log("x", x) // 1
console.log("length", length) // 1
Notice how the length is one, because you start with 2 elements, then get rid of 1, and 2 - 1 is 1.
Why does this happen? ... means "rest", so it's the rest of the properties.
const arr = [1, 2]
const [x, ...rest] = arr
console.log("x", x) // 1
console.log("rest", rest) // [2]
When you use { } you get a property off of the rest, so it's the same as saying
const arr = [1, 2]
const [x, ...rest] = arr
console.log("x", x) // 1
const {length} = rest;
console.log("rest", rest) // [2]
console.log("length", length) // 1
I want to fill the middle area with recursion. What is the best way to do this? I want a solution with recursion not other. How can I do that the left right top and bottom are checked?
'use strict'
let bitmap = [
'.....******.....', //.....******.....
'....*......*....', //....*++++++*....
'...*........*...', //...*++++++++*....
'....*......*....', //....*++++++*....
'.....******.....', //.....******.....
];
bitmap = bitmap.map((row) => [...row]);
const showOnPosition = (x, y) => bitmap[y][x];
//console.log(showOnPosition(4, 1));
const changeSymbol = (x, y) => (bitmap[y][x] = '+');
//console.log(changeSymbol(6, 1));
const fill = (x, y) => {
// painted?
if (showOnPosition(x, y) === '*') {
return bitmap.map((row) => row.join('')).join('\n');
} else {
// paint!
changeSymbol(x, y);
return bitmap.map((row) => row.join('')).join('\n');
}
};
console.log(fill(6, 1));
I want to check the surrounding elements and if they are not a . they should be replaced by a +. Similar to a floodFill or here only by recursion.
This is how it should look like:
.....******.....
....*++++++*....
...*++++++++*....
....*++++++*....
.....******.....
To fill recursively, you'll have to iterate over the array of one-cell differences - that is, over
[[0, 1], [0, -1], [1, 0], [-1, 0]]
From those, identify the new X and Y, and call fill again if that coordinate hasn't been filled before. Create a Set containing filled coordinates and pass it along.
Since [6] doesn't exist on the outer array, it sounds like you also need to switch around the X and Y coordinates in the parameters. (fill(6, 1) should refer to bitmap[1][6])
'use strict'
const bitmap = [
'.....******.....', //.....******.....
'....*......*....', //....*++++++*....
'...*........*...', //...*++++++++*....
'....*......*....', //....*++++++*....
'.....******.....', //.....******.....
]
.map(row => [...row]);
const fill = (y, x, filled = new Set()) => {
if (bitmap[x][y] === '.') {
// paint!
bitmap[x][y] = '+';
// recurse
for (const [xDiff, yDiff] of [[0, 1], [0, -1], [1, 0], [-1, 0]]) {
const newX = x + xDiff;
const newY = y + yDiff;
const key = `${newX}_${newY}`;
if (!filled.has(key) && bitmap[newX]) {
filled.add(key);
fill(newY, newX, filled);
}
}
}
return bitmap.map((row) => row.join('')).join('\n');
};
console.log(fill(6, 1));
I am trying to extract variables using object destructuring but those variables already exist, something like this
const x=1, y=2 // Those should be 1 and 2
const {x,y} = complexPoint
const point = {x,y}
Is there any way to do this without renaming the destructuring variables?.
Some like this and updating point avoiding const definition?
const point = {x,y} = complexPoint
The expected result should be as using object destructuring
const x=1, y=2 // Those should be 1 and 2
const point = {
x:complexPoint.x,
y:complexPoint.y
}
You can do this with array destructuring, i.e.:
const complexPoint = [1,2];
let x, y;
[x,y] = complexPoint;
As for object destructuring, an equivalent syntax would not work as it would throw off the interpreter:
const complexPoint = {x:1,y:2};
let x, y;
{x,y} = complexPoint; // THIS WOULD NOT WORK
A workaround could be:
const complexPoint = {x:1,y:2};
let x, y;
[x,y] = [complexPoint.x, complexPoint.y];
// Or
[x,y] = Object.values(complexPoint);
UPDATE:
It appears you can destructure an object into existing variables by wrapping the assignment in parenthesis and turning it into an expression. So this should work:
const complexPoint = {x:1,y:2};
let x, y;
({x,y} = complexPoint); // THIS WILL WORK
here it can be done like this.
const complexPoint = {x: 1, y: 2, z: 3};
const simplePoint = ({x, y}) => ({x, y});
const point = simplePoint(complexPoint);
console.log(point);
In a single line looks like this:
const complexPoint = {x: 1, y: 2, z: 3};
// can be written as
const point2 = (({x, y}) => ({x, y}))(complexPoint);
console.log(point2);
It's not 100% clear to me what you want to do.
If you want to update point with two properties of complexPoint
You can actually destructure an object into anything that is assignable. Most often you will destructure into variables, but you can also destructure into properties.
Example:
const point = {x: 1, y: 2};
const otherPoint = {x:3, y: 4};
({x: point.x, y: point.y} = otherPoint);
// ^ ^
// parenthesis are necessary otherwise the runtime will interpret {
// as the start of a block
console.log(point);
Of course this can become difficult to read the more properties you have. You can also just assign them directly, the good old fashioned way:
point.x = otherPoint.x;
point.y = otherPoint.y;
Or with a loop:
for (const prop of ['x','y']) {
point[prop] = otherPoint[prop];
}
If you want to create a new object from an existing object
Create a helper function to "pick" the properties from the existing object. Such a function is provided here.
const point = pick(otherPoint, 'x', 'y');
After the destructuring, you can use the destructured data and save it as an object to another variable.
If we have this object:
const obj={"property":"value","property2":"value"};
You can destructure data from it like this:
const {property}=obj;
If we want to assign the destructured data only to another variable, We would do this:
const newVariable ={property};
Now the newVariable value will be equal to:
{"property":"value"}
It's not 100% clear to me how this piece of code works:
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
// OUTPUT: [0, 1, 2, 3, 4]
I'm deconstructing the array a using the ... operator.
I am expecting that in second line a bunch of assignments will take place.
The x will be assigned to 0, y will be assigned to ...a (which passes the elements in the array a as individual values).
It's not clear to me, though, how the ...a get assigned to 4. In fact, JS throws an exception when doing:
...a = 4;
// Uncaught SyntaxError: Rest parameter may not have a default initializer
Why does this code output the modified array with the end 4, instead of throwing an exception? How does this work exactly?
It is executed like following
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
[x, y, ...a ] = [0, 1, 2, 3, 4];
which means first value in RHS array is assigned to x, second value in RHS array is assigned to y and the remaining values are assigned to a.
Hence, value of x is 0, y is 1 and a is [2, 3, 4]
It's not clear to me, though, how the ...a get assigned to 4.
It's not.
Lets split things up a little:
On the right hand side of the assignment you are using an array literal with a spread element. The value of a is "flattened" into the new array.
Thus, [0, ...a, 4] is is equivalent to [0].concat(a, [4]). The result is a new array.
var a = [1, 2, 3];
console.log('spread element', [0, ...a, 4]);
console.log('concat', [0].concat(a, [4]));
On the left hand side you are using array destructuring with a rest element. [x, y, ...a ] means
assign the first value of the iterable to x
assign the second value of the iterable to y
assign the remaining values of the iterable as an array to a
These two are equivalent:
var a = [1,2,3,4];
var [x, y, ...z] = a;
console.log('destructuring', x, y, z);
var x = a[0];
var y = a[1];
var z = a.slice(2);
console.log('manual + slice', x, y, z);
Of course combining these two is perfectly fine. In an assignment, the left hand side doesn't care what how the right hand side is computed and vice versa.
What's confusing about your example is that you are using a again in the destructuring assignment, but that's the same as overriding the value of a with a new value. However the end result is
[0, ...a, 4] results in [0,1,2,3,4] therefor
x has value 0
y has value 1
a has value [2,3,4]
In fact, JS throws an exception when doing: ...a = 4;
The error message you are getting is strange. It should really be just a syntax error.
... by itself doesn't mean anything. It's not an operator, it's a punctuator (like ; or ,) that has a different meaning depending on the context it is used (and allowed).
See also What is SpreadElement in ECMAScript documentation? Is it the same as Spread operator at MDN?
...a is either equal to .slice(start, end) (left, destructuring) or to .concat(a) (right, spreading):
[0, ...a, 4]
is equal to:
[0].concat(a).concat([4]) // [0,1,2,3,4]
Whereas:
[x, y, ...a] = array
Is equal to:
x = array[0];
y = array[1];
a = array.slice(2);
In the first example, spread ie ... in LHS acts as gatherer whereas on the RHS it acts as spread/rest. IE you are assigning value to variable a when it is on LHS.
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
console.log(a)
Let's go step by step:
Let's start with RHS. Doing [0, ...a, 4] will generate [0, 1, 2, 3, 4]. See for yourself:
var a = [1, 2, 3];
console.log([0, ...a, 4]);
Now, the LHS is the side where assignment is taking place. On RHS, imagine any variable with spread operator as an array ready to be assigned new values.
So, we are trying to assign [0, 1, 2, 3, 4] to a variable x, then to y and the rest to array a (in that order). So, array a will have whatever will be left after first two assignments (ie 2, 3, 4).
var a = [1, 2, 3];
// a will get overwritten
[x, y, ...a ] = [0, 1, 2, 3, 4];
// same as [x, y, ...a ] = [0, ...a, 4];
console.log(a);
Finally, coming to your last question: "It's not clear to me, though, how the ...a get assigned to 4? "
Answer: It is not. But if you do something like [...a] = [4], it will result in an array named a containing [4].
You can read more about spread syntax here (MDN) and here (YDKJS).
When I try to make a function to swap 2 arrays, the original arrays are left unaltered.
function swap(x, y) {
var temp = x; x = y; y = temp;
}
u=[1, 0];
v=[0, 1];
swap(u, v);
console.log(u);
console.log(v);
This results in u as [1, 0] and v as [0, 1]. The values haven't been changed after the function call to swap.
On the other hand, if I do this without a function call:
u=[1, 0];
v=[0, 1];
var temp = u;
u = v;
v = temp;
console.log(u);
console.log(v);
Then they're swapped correctly, with u as [0, 1] and v as [1, 0].
I thought Javascript arrays are passed by reference, not by value. Am I misunderstanding something here?
Javascript does not have the ability to pass a reference to the u and v variables themselves. So, no assignment to x or y in your swap() function will change what is assigned to u or v. Javascript passes a reference to the object that u and v hold. Thus, you can't change the u and v variables from within swap(). You can change the contents of the object that they point to and thus properties of the object that u and v point to can be modified.
Since I have a C/C++ background, I think of what Javascript does when passing objects as "pass by pointer". When you call swap(u, v), what is passed to the swap() function is a pointer to the array that u also points to. So, now you have two variables u and x both "pointing" at the same array. Thus, if you modify that actual array, then since u points at that same array, both will see the modification. But, nothing you do inside the swap() function can change what object u or v actually point to.
In Javascript, the only way to change what object the original variables point to is to make them properties of an object and pass the object like this:
function swap(obj, x, y) {
var temp = obj[x]; obj[x] = obj[y]; obj[y] = temp;
}
var container = {};
container.u = [1, 0];
container.v = [0, 1];
swap(container, "u", "v");
console.log(container.u);
console.log(container.v);
If you don't mind rewriting both arrays from scratch, you can copy all the contents of one array to a temporary, then copy one array over to the other and then copy the temporary array contents back to the first original. This is not very efficient and there is probably a better way to solve your original problem, but it can be done.
function swap(x, y) {
// remove all elements from x into a temporary array
var temp = x.splice(0, x.length);
// then copy y into x
x.push.apply(x, y);
// clear y, then copy temp into it
y.length = 0;
y.push.apply(y, temp);
}
Getting the terminology on these "reference/value" questions is tricky, but I will do my best.
What you have for your Object / Array variables are really just references. Unlike C++, saying "x = y" does not actually copy the object's variables over to a new memory location. Internally, it's just copying a pointer location over. The language does not have constructions to "automatically recreate" something like an object or array in a new instance; if for some reason, you want to maintain two copies of an array, you will need to explicitly create it then copy over values (ie, = []; or = new Array(); or a copying function like = oldArray.map(...))
A little code example that might conceptually help. These same rules apply between objects and arrays.
a = {}; // In case you haven't seen it, this is like shorthand of "new Object();"
b = {};
c = b;
console.log(a === b); // false
console.log(b === c); // true
b.property = "hello";
console.log(c.property) // hello
Just like Java, JavaScript is pass-by-value only. Assigning to local variables in a function never has any effect on anything outside the function.
They are passed by reference, but they are also assigned by reference. When you write x = y you aren't modifying either of the arrays, you're just making your local variable x refer to the same array as y.
If you want to swap the array contents, you have to modify the arrays themselves:
function swap(x,y) {
var temp = x.slice(0);
x.length = 0;
[].push.apply( x, y );
y.length = 0;
[].push.apply( y, temp );
}
Feb 2022 Solution to Swap 2 Entire Array Contents.
You can use destructuring to swap the 2 arrays:
let a = [ 1, 2, 3, 4 ];
let b = ['a','b','c','d'];
[a,b] = [b,a]; // swap
console.log(a); // ["a", "b", "c", "d"]
console.log(b); // [1, 2, 3, 4, 5]
let a = [ 1, 2, 3, 4 ];
let b = ['a','b','c','d'];
[a,b] = [b,a]; // swap
console.log(a); // ["a", "b", "c", "d"]
console.log(b); // [1, 2, 3, 4, 5]
To swap 2 arrays you may use
Version 1
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
let tempArr = [arrA, arrB]; // constructing new array
arrA = tempArr [1];
arrB = tempArr [0];
Version 1 (shorthanded)
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
// RHS : construction a new array
// LHS : destruction of array
[arrB, arrA ] = [arrA, arrB];
Version 2 (spread operator)
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
let arrC = [...arrB]
arrB = [...arrA]
arrA = [...arrC]