Does 'array[array.length - 1] = array.pop()' yield undefined behavior? - javascript

I've been trying to implement a method which takes an array and an index as input, copies the last element of the given array into the entry at the given index, and then truncates the last element (i.e., sets the array shorter by one).
Here is the function which I was given as a suggestion for this task:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
I then tested this function to make sure that it works on any given index:
for (let index = 0; index < 5; index++) {
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, index);
console.log(array);
}
And I found it that it works correctly for all but the last index:
[ 4, 1, 2, 3 ]
[ 0, 4, 2, 3 ]
[ 0, 1, 4, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 1, 2, 3, 4 ]
In other words, for the last element, it pops it out of the array, but then rewrites it into the array.
What strikes me, is that this line:
array[index] = array.pop();
Which is equivalent to this line when executed on the last element:
array[array.length - 1] = array.pop();
Could not have possibly resulted with the contents of array being equal to [ 0, 1, 2, 3, 4 ].
The way I see it, there are two options here:
The statement array.pop() is executed before the expression array.length - 1 is evaluated
The statement array.pop() is executed after the expression array.length - 1 is evaluated
In the first case, the contents of array would change as follows:
[ 0, 1, 2, 3, 4 ] // initial state
[ 0, 1, 2, 3 ] // after popping 4
[ 0, 1, 2, 4 ] // after assignment
In the second case, it should have triggered some sort of memory-access violation, because there would be an attempt to write into the 5th entry in the array (array[4]), when the length of the array is only 4 entries.
I know that NodeJS could be applying some memory-management scheme which would somehow allow this to complete without an "array index out of bound" exception, but I still don't get why it would let this operation "get away with it", and moreover, why the result is what it is.
Is the line array[array.length - 1] = array.pop() possibly undefined behavior?
Is there even such thing as undefined behavior in JavaScript?

The second option: The statement array.pop() is executed after the expression
array.length - 1 is evaluated
This is indeed what happens. JS evaluation is always left-to-right. It evaluates the array.length - 1 to index 4 of the array, then pops the last element from the array, then assigns that element to index 4.
In the second case, it should have triggered some sort of memory-access violation, because there would be an attempt to write into the 5th entry in the array (array[4]), when the length of the array is only 4 entries.
No, there are no memory access violations in JS. It just creates a new property again, and changes the length of the array accordingly. It's just the same as what happens with the code
const array = [0, 1, 2, 3, 4];
const element = array.pop();
console.log(JSON.stringify(array)); // [0,1,2,3]
array[4] = element;
console.log(JSON.stringify(array)); // [0,1,2,3,4]
Similarly, filling an array uses the same feature:
const array = [];
for (let i=0; i<5; i++)
array[i] = i; // no memory access violation
console.log(array.length); // 5

The last index results in [0, 1, 2, 3, <empty>, 4], not [ 0, 1, 2, 3, 4 ]:
function swapWithLastAndRemove(array, index) {
array[index] = array.pop();
}
var array = [0, 1, 2, 3, 4];
swapWithLastAndRemove(array, 5);
console.log(array);
Execution order isn't really something to worry about here, because the index is constant - the parameter index is never reassigned, so the number passed into swapWithLastAndRemove will always be the index, which the popped item is assigned to. Considering it introduces more confusion than necessary - easier to just keep in mind that the passed index is never reassigned.
What's going on is:
(1) The last item of the array is popped off (4), resulting in the array becoming [0, 1, 2, 3]
(2) The index passed in was 5, so array[5] is set to 4. The operation is equivalent to:
const arr = [0, 1, 2, 3];
arr[5] = 4;
console.log(arr);
This is resulting in a sparse array - there's no item at index 4 anymore. It's not undefined behavior, it's just weird, because sparse arrays are weird.
Is there even such thing as undefined behavior in Javascript?
Yes, see here.

Related

Why this foreach loop not popping all the elements?

its only popping twice rather than 4 times
i tried for of loop but still same result.
var arr = [1, 2, 3, 4];
arr.forEach((val, index, io) => console.log(val, index, io.pop()))
Your code works as expected.
Array.pop() method removes the last element from an array and returns that element. This method changes the array from which it has been called.
Array.pop() returns the removed element from the array.
When the iteration index is 0, it will iterate through the array [1, 2, 3, 4], it removes 4 on the first iteration, which makes the length of the array as 3.
When the iteration index is 1, iteration will be done on Array [1, 2, 3] and it removes 3 from this array.
Now the length of Array is 2 and the iteration has already completed twice and the loop exits.
Thats why your loop excecutes only twice.
const arr = [1, 2, 3, 4];
arr.forEach((val, index, io) => {
console.log(`Iterating ${index + 1}`);
console.log(val, index, io.pop());
console.log(`Array After Iteration ${io}`);
});
console.log(`Final Array ${arr}`);
var arr = [1, 2, 3, 4];
arr.forEach((val, index, io) => console.log(val, index, io.pop()))
Your code is doing what it should
val | index | io.pop() | Modified Array
1 0 4 [1, 2, 3]
2 1 3 [1, 2]
// since it already reached at 2 no other element needs to be traversed and hence iteration stops.
Each time you are logging it is removing the last element of the array and moving the forward as well.
The callback is executed only for the elements that are present in the array and hence as your elements are being removed with every log you remain with two elements and hence it runs only two times.
Hope This helps. !✌

JS - For Loops Pushing Array

I have an initial array,
I've been trying to change values (orders) by using pop, splice methods inside a for loop and finally I push this array to the container array.
However every time initial array is values are pushed. When I wrote console.log(initial) before push method, I can see initial array has been changed but it is not pushed to the container.
I also tried to slow down the process by using settimeout for push method but this didnt work. It is not slowing down. I guess this code is invoked immediately
I would like to learn what is going on here ? Why I have this kind of problem and what is the solution to get rid of that.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial);
}
return schedulePattern;
}
**Console.log**
(4) [1, 2, 3, 4]
(4) [1, 4, 2, 3]
(4) [1, 3, 4, 2]
(3) [Array(4), Array(4), Array(4)]
0 : (4) [1, 3, 4, 2]
1 : (4) [1, 3, 4, 2]
2 : (4) [1, 3, 4, 2]
length : 3
When you push initial into schedulePattern, it's going to be a bunch of references to the same Array object. You can push a copy of the array instead if you want to preserve its current contents:
schedulePattern.push(initial.slice(0));
Good answer on reference types versus value types here: https://stackoverflow.com/a/13266769/119549
When you push the array to schedulepattern, you are passing a reference to it.
you have to "clone" the array.
use the slice function.
function trial(){
let schedulePattern = [];
let initial = [1,3,4,2];
for(let i = 0; i < 3; i++){
let temp = initial.pop();
initial.splice(1,0,temp);
console.log(initial);
schedulePattern.push(initial.slice());
}
return schedulePattern;
}
​
You have to know that arrays are mutable objects. What does it mean? It means what is happening to you, you are copying the reference of the object and modifying it.
const array = [1,2,3]
const copy = array;
copy.push(4);
console.log(array); // [1, 2, 3, 4]
console.log(copy); // [1, 2, 3, 4]
There are a lot of methods in Javascript which provide you the way you are looking for. In other words, create a new array copy to work properly without modify the root.
const array = [1,2,3]
const copy = Array.from(array);
copy.push(4);
console.log(array); // [1, 2, 3]
console.log(copy); // [1, 2, 3, 4]
I encourage you to take a look at Array methods to increase your knowledge to take the best decision about using the different options you have.

forEach seems to be working for push() function but didn't work for pop() in JavaScript. can someone tell me what I am doing wrong

//code1
let a= [1, 3 , 4, 6];
[7, 8 , 9].forEach(l => a.push(l));
console.log(a);
// [1, 3, 4, 6, 7, 8, 9 ]
1.it worked for push() function
//code2
let a= [1, 3 , 4, 6];
a.forEach(l => a.pop(l));
console.log(a);
//[ 1, 3 ]
2. didn't work for pop() though
Javascript Array.pop() removes the last element from the array and returns that.
Example:
var arr = [1,2,3]
arr.pop(); // returns 3
Reference
If you want to remove a element with specific value than try something like:
var arr = [1, 2, 3];
var index = arr.indexOf(1);
if (index > -1) {
array.splice(index, 1);
}
var arr = [1, 2, 3, 4];
console.log(arr.pop());
var index = arr.indexOf(2);
if (index > -1) {
arr.splice(index, 1);
}
console.log(arr)
forEach automatically extracts the elements one by one and gives them to you
It starts from the beginning of the array, and does them all.
It doesn't delete elements from the array.
a = [1, 3, 4, 6];
a.forEach(item => console.log(item));
// output is in forwards order
// and 'a' retains original contents
pop() extracts and deletes one element for you
It starts from the end of the array, and does only one.
It deletes the element from the array.
a = [1, 3, 4, 6];
while (a.length > 0) {
console.log(a.pop())
}
// items come out in reverse order
// and 'a' is being emptied so it is [] at the end
Choose your method
Do you want the last element actually removed from the array? This is what you would want if you were implementing a stack, for example. In that case, use ".pop()".
This gets one element from the end of the array and deletes it from the array.
Or do you want to just look at each element in turn from the array (starting at the beginning), without changing the array itself. This is a commoner situation. In this case, use ".forEach"

Find largest adjacent product in array (JavaScript)

I'm trying to understand the following solution for finding the largest adjacent product in any given array.
Example:
For inputArray = [3, 6, -2, -5, 7, 3], the output should be
adjacentElementsProduct(inputArray) = 21.
7 and 3 produce the largest product.
Possible solution in JS:
function adjacentElementsProduct(arr) {
return Math.max(...arr.slice(1).map((x,i)=>[x*arr[i]]))
}
I am having a hard time understanding two things:
What do the three dots exactly do and how does this get passed into the function? Is there any way to write this in a more understandable way? I know that is the "spread syntax" feature in ES6, but still don't understand completely.
Why do we insert "1" as argument to slice? My first though was to input "0", because we want to start at the start, then loop through everything, and see which adjacent product is the largest.
I'd appreciate any advice, links and explanations.
Thanks.
Cheers!
1. What do the three dots exactly do and how does this get passed into the function? Is there any way to write this in a more understandable way? I know that is some kind of "spread" feature in ES6, but still don't understand completely.
The Math#max needs a list of numbers as parameters, and map produces an array. The spread syntax is used to convert an array to be expanded to a list of parameters.
const arr = [1, 2, 3];
console.log('max on array', Math.max(arr));
console.log('max on list of parameters', Math.max(...arr));
In this case you can use Function#apply to convert the array to a list of parameters. I find it less readable, however.
const arr = [1, 2, 3];
console.log(Math.max.apply(Math, arr));
2. Why do we insert "1" as argument to slice? My first though was to input "0", because we want to start at the start, then loop through everything, and see which adjacent product is the largest.
Lets break down the iteration order of the 2 arrays.
[3, 6, -2, -5, 7, 3] // inputArray
[6, -2, -5, 7, 3] // inputArray.slice(1)
Now on each iteration of inputArray.slice(1):
x: 6, i = 0, arr[0] = 3
x: -2, i = 1, arr[1] = 6
x: -5, i = 2, arr[2] = -2
Since the inputArray.slice(1) array starts from the 2nd element of the inputArray, the index (i) points to the 1st element of the inputArray. And the result is an array of products of 2 adjacent numbers.
var biggestProduct = inputArray[0] * inputArray[1];
for (i=0; i<inputArray.length-1 ; ++i)
{
console.log(biggestProduct)
if ((inputArray[i] * inputArray[i+1] ) > biggestProduct)
{
biggestProduct = inputArray[i] * inputArray[i+1]
}
}
return biggestProduct;
Note: I've declared a variable that consists of 2 input arrays with index number then starts a for loop that indicates input array with his index number, so by that he will go throw all the index number of the array (one of them raised by one so that they won't be at the same value). and at the end of the code, you have the if statement.
You may simply do as follows;
function getNeigboringMaxProduct([x,...xs], r = -Infinity){
var p = x * xs[0];
return xs.length ? getNeigboringMaxProduct(xs, p > r ? p : r)
: r;
}
var arr = [3, 6, -2, -5, 7, 3],
res = getNeigboringMaxProduct(arr);
console.log(res);

Newbie to Javascript: how to sum specific numbers in an array

Please help, I've been looking for an answer for far too long.
I'm trying to create an array using push method to insert the numbers
0 to 10 into positions 0 through 10 of the numbers array you just initialized above.
I did this:
var numbers = [];
for(var i = 0; i < 10; i++) {
numbers.push(i);
console.log(numbers);
And got this result, which I think is correct but not 100% sure:
[ 0 ]
[ 0, 1 ]
[ 0, 1, 2 ]
[ 0, 1, 2, 3 ]
[ 0, 1, 2, 3, 4 ]
[ 0, 1, 2, 3, 4, 5 ]
[ 0, 1, 2, 3, 4, 5, 6 ]
[ 0, 1, 2, 3, 4, 5, 6, 7 ]
[ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
0
Then I am to test the array push method by printing the sum of the values at
position 3 and 6 of the array (use the console.log() function to print to the console).
The outputted value should be 9.
I am so stuck on this point and cannot find a sample anywhere of how to accomplish this. I thought it might be something like:
console.log(numbers(sum[3, 6]);
If you want to have a sum() function, then try the following:
function sum(x, y) {
return x + y;
}
console.log(sum(numbers[3], numbers[6]));
Here's a Fiddle: https://jsfiddle.net/7181h1ok/
To sum the values of two indices of an array, you use the + addition operator in the following fashion:
var numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
var sum = numbers[3] + numbers[6]; //adds the value in index 3 of the numbers array to the value in index 6 of the numbers array.
console.log(sum); //prints the sum to the console.
As a note, if you are unfamiliar with JavaScript and/or its operators, there's useful documentation at w3schools that can get you started.
First, let's convert your code to a little bit better style:
const numbers = [];
for (let i = 0; i < 10; i++) {
numbers.push(i);
console.log(numbers);
}
Note: I made numbers a const instead of a var, since you don't change it. I also made i a let binding instead of a var. In general, var is a legacy and should never be used. Use const instead if at all possible, otherwise use let.
Also, I inserted a space after the for keyword. It is generally recommended to separate the parentheses which enclose the header of a control structure keyword (if, while, for, etc.) with a space, to make it visually distinct from the parentheses for the argument list of a function call, which has no space.
Secondly: Your result is not correct. (Hint: how many numbers are the numbers 0 to 10?) It should include the numbers 0 to 10, but it only includes the numbers 0 to 9. You have what is generally called an off-by-one-error. These errors are very common when dealing with trying to manage loop indices manually. This is the fix:
const numbers = [];
for (let i = 0; i <= 10; i++) {
// ↑
numbers.push(i);
console.log(numbers);
}
Most modern programming languages have better alternatives than dealing with loop indices manually in the form of higher-level abstractions such as iterators, maps, and folds. Unfortunately, ECMAScript doesn't have a Range datatype, otherwise this could simply be expressed as converting a Range to an Array.
If ECMAScript did have a Range datatype, it could for example look like one of these:
const numbers = Range(0, 10).toArray()
const numbers = Array.from(Range(0, 10))
Here is an alternative for creating the numbers Array that doesn't involve manually managing loop indices, but still requires knowing that 0 to 10 are 11 numbers:
const numbers = Array.from({length: 11}, (_, i) => i)
If you want to add the numbers at indices 3 and 6, you can simply dereference indices 3 and 6 and add the results:
console.log(numbers[3] + numbers[6])
In the comments, you asked how you would add up all numbers in the Array. Combining the elements of a collection using a binary operator is called a fold or reduce, and ECMAScript supports it out-of-the-box:
console.log(numbers.reduce((acc, el) => acc + el));
Note how there is no explicit loop, thus no explicit management of loop indices. It is simply impossible to make an off-by-one-error here.
It will be: console.log((+numbers[3]) + (+numbers[6]));
Typically, it should be console.log(numbers[3] + numbers[6]); but there's sometimes a issue that results in 36 instead of 9. The extra + signs tell javascript that it is a number.
NOTE: Remember that the first number is numbers[0]. The array starts with 0!

Categories

Resources