Getting array length by destructuring assignment - javascript

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

Related

Assign full array to destructuring variable

In Clojure destructuring it's possible to also have a binding for the full array using :as, is something similar possible in Javascript?
For example,
const queue = [[1, 2]];
const [x, y :as pos] = queue.shift();
where x would be 1, y would be 2 and pos would be [1, 2]?
or is an extra in-between step necessary, like
const queue = [[1, 2]];
const pos = queue.shift();
const [x, y] = pos;
You can't get the parent and nested properties at the same time during destructuring.
I'd do it separately for readability
const queue = [ [1, 2] ],
pos = queue.shift(),
[x, y] = pos;
BUT, it is possible to do it in single line. If you destructure the array like an object. Get the 0 property to a variable and destructure the nested array
const queue = [ [1, 2] ];
const { 0: pos, 0: [x, y] } = queue
console.log(x, y)
console.log(pos)

In Javascript how do I create a secondary array in a specific index?

How do I create a subarray from an existing array in Javascript?
For example;
Arr = [5,2,1,2]
Then I want to insert 8 in position 1 of Arr, but keep the original value 2. So arr can become:
Arr = [5,[2,8],1,2]
I tried using concat, which sort of did something but duplicates all values.
Bear in mind that this can grow e.g. Arr = [5,[2,8,3,4],1,[2,3]]
Thanks!
You could assign the concatinated values.
const
addAt = (array, value, index) => array[index] = [].concat(array[index], value),
array = [5, 2, 1, 2];
addAt(array, 8, 1);
console.log(array)
addAt(array, 3, 1);
console.log(array)
.as-console-wrapper { max-height: 100% !important; top: 0; }
There are several different ways to do this, but you can reassign the current array index to an array like so:
Arr[1] = [Arr[1], 8]. Then if you wanted to continue adding to the array at index 1 in Arr, you could do something like Arr[1].push(x).
You could do something like this (Probably not the best answer, but may be helpful)
const addIntoArray = (arr, toAdd, index) => {
arr[index] = typeof arr[index] == "number" ? [arr[index], toAdd] : [...arr[index], toAdd];
return arr
}
Arr = [5,2,1,2]
console.log(Arr); // [ 5, 2, 1, 2 ]
addIntoArray(Arr, 1, 1)
console.log(Arr); // [ 5, [ 2, 1 ], 1, 2 ]
addIntoArray(Arr, 3, 1)
console.log(Arr) // [ 5, [ 2, 1, 3 ], 1, 2 ]
Basically ...arr expands the array and then we add toAdd at it's end and [arr[index], toAdd] creates an array with the first element as the number and the second the new element. (It modifies the original array, as shown in the example, so pay attention as it may lead to bugs)
The typeof arr[index] == "number"is just a simple/generic typecheck to see if there isn't an array already
This function should satisfy your conditions at basic level
// a - array, v - value to insert, i - position to insert
const avi = (a, v, i) => {
r = a.slice(0, i);
r.push([a[i], v]);
a.slice(i+1, a.length).forEach(e => r.push(e));
return r;
}
console.log(JSON.stringify(avi([5,2,1,2], 8, 1)))
//=> "[5,[2,8],1,2]"

Get array of array numbers

I have an array like this one:
let array = [14, 42, 1, 3]
And I would like to get the arrays number mapped to this:
[1, 0, 3, 2]
Here is the reason:
1: because 14 is the second biggest number
0: because 42 is the biggest number
3: ...
What I have tried so far:
let sort = (array) => {
let result = []
let x = array.slice(0).sort((a, b) => b - a)
for (let elem of x) {
result.push(array.indexOf(elem))
}
console.log(result)
}
// Working
sort([14, 42, 1, 3]) // [1, 0, 3, 2]
// Not working, includes the index "0" two times
sort([14, 42, 14, 3]) // [1, 0, 0, 3]
// Expected: [1, 0, 2, 3]
You could take the indices and sort them by taking the value from the given array.
const sort = array => [...array.keys()].sort((a, b) => array[b] - array[a]);
console.log(sort([14, 42, 1, 3]));
console.log(sort([14, 42, 14, 3]));
It's because indexOf stops when it finds it's first result.
You could try to change the value to null once it's located the first time, or compare value to values already in the result and ignore those values.
let sort = (array) => {
let result = []
let x = array.slice(0).sort((a, b) => b - a)
for (let elem of x) {
result.push(array.indexOf(elem))
array[array.indexOf(elem)] = null;
}
console.log(result)
}
let sort = (arr) => {
let arr2 = arr.slice().sort((a, b) => b - a);
return arr.map((val) => {
return arr2.indexOf(val);
})
}
console.log(sort([14, 42, 1, 3]));
You can use a tracker object, map value and indexes as key/value pair and when looping though the array take the first index from respective key and shift it as well
let sort = (array) => {
let result = []
let x = array.slice(0).sort((a, b) => b - a)
let tracker = x.reduce((op,inp,i)=>{
op[inp] = op[inp] || []
op[inp].push(i)
return op
},{})
for (let elem of array) {
let val = tracker[elem][0]
tracker[elem].shift()
result.push(val)
}
console.log(result)
}
sort([14, 42, 1, 3]) // working
sort([14, 42, 14, 3]) // includes the index "0" two times
(() => {
function getBiggestOrder (nums) {
const lookup = {}
const result = nums.slice(0).sort((a, b) => b - a).map((num, i) => {
lookup[num] = i
return num
})
return nums.map(n => lookup[n])
}
const op = getBiggestOrder([14, 42, 1, 3])
console.log(op)
return op
})()
You are basically numbering the numbers from biggest to smallest. sort them in a duplicate array from biggest to smallest. And replace the original array numbers with their index in the duplicate array.
Original = [14, 42, 1, 3]
Duplicate = [42, 14, 3, 1]
Duplicate indexes are [0, 1, 2, 3]
so find 42 in the first array and replace it with the index of the 42 in the duplicate array, etc.

Creating an array from ES6 generator

Let's say I want to assign the result of an ES6 generator to an array variable.
function* gen() {
for(let i = 0; i < 3; i++) {
yield i;
}
}
let [...b] = gen();
console.log(b); // [0, 1, 2]
Here b will be assigned [0, 1, 2]. Why does this work?
A generator, when invoked, returns an iterator. We could for example loop through the iterator with a for … of loop:
for (const item of gen()) {
console.log(item);
}
Which would just go through each item in the generator:
0
1
2
The same things happens, if we invoke the spread syntax:
const res = [...gen()];
res would then be:
[0, 1, 2]
In your example, you're using a destructuring assignment. Destructuring with the bracket syntax invokes the iterable to get the values (same principle for arrays). Example:
const [a, b] = gen();
// a: 0, b: 1
Since you're using the rest syntax, you're basically saying: Give me all the values that are left in the iterator, and save them in the variable b:
let [...b] = gen();
// b: [0, 1, 2]
This works on any iterable:
const [a, b, ...c] = 'hello';
// a: 'h', b: 'e', c: 'llo'
Personally, I find the following a lot easier to reason about:
const res = [...gen()];
Well I think I found the answer on this post. The ... operator here is the rest operator. When used to destructure an array, it will assign all unassigned elements of the array being destructured to another array. The rest operator must be used on the last item in the list of variables receiving the destructured values. For example:
let a = [1, 2, 3, 4, 5];
let [first, second, ...remainder] = a;
console.log(first); // 1
console.log(second); // 2
console.log(remainder); // [3, 4, 5]
In the question, since b is the only thing being destructured and its using the rest operator, the entire array is assigned to it.
ES6 appears to run the generator and turn the result into an array on the right hand side of the =.
For actually creating an array from a generator function there are some answers here which are pretty good.
I personally prefer Array.from:
function* gen() {
for(let i = 0; i < 3; i++) {
yield i;
}
}
const arr = Array.from(gen());
console.log(arr); // [0, 1, 2]

Swap entire arrays in Javascript

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]

Categories

Resources