3

I'm reading through Eloquent Javascript and facing one of the exercises, I found a rather odd behavior. (at least for me)

the exercise asks to create a function in order to reverse an array. I thought I could loop over the array and each time pop one item from the original array and push it into a temporary array that is going to finally be returned. but as I'm looping over the array either with a for-of loop or typical sequential loop, last item is not transferred.

can someone tell me what happens exactly?

const reverseArray = function(array) {
let rev = [];
for (let x = 0; x <= array.length; x++) {

    rev.push(array.pop());
    console.log(rev, array)
}
return rev;
};


console.log(reverseArray(["A", "B", "C"]));

output:

["C"] ["A", "B"]
["C", "B"] ["A"]
["C", "B"]
  • You should also be mindful of the "off by one bug" (obob) when using `i <= array.length` this answer will illustrate this point: [obob](https://stackoverflow.com/questions/2939869/what-is-exactly-the-off-by-one-errors-in-the-while-loop) – Matt D. Webb Mar 17 '18 at 09:51

6 Answers6

5

When the pop() applies on array it decreases the length of the array so when the loop runs it finds one item less than the previous array length. Thus, what you can do is simply assign the length of the array in a variable and use that in the comparison of the for loop:

let rev = [];
const reverseArray = function(array) {
var length = array.length;
for (let x = 0; x < length; x++) {
  rev.push(array.pop());
}
return rev;
};


console.log(reverseArray(["A", "B", "C"]));
Ankit Agarwal
  • 30,378
  • 5
  • 37
  • 62
3

As the pop() method remove the pop'ed item from the array, use a while loop instead of for

let rev = [];
const reverseArray = function(array) {
    while (array.length > 0) {    // or just "(array.length)"
      rev.push(array.pop());
    }
    return rev;
}

console.log(reverseArray(["A", "B", "C"]));

With closure you can save yourself an extra global variable

const reverseArray = function(array) {
  return (function(a_in,a_out) {
    while (a_in.length > 0) {    // or just "(a_in.length)"
      a_out.push(a_in.pop());
    }
    return a_out;
  })(array,[]);
}

console.log(reverseArray(["A", "B", "C"]));

Or if the Array.reverse() method is allowed

const reverseArray = function(array) {
    return array.reverse();
}

console.log(reverseArray(["A", "B", "C"]));
Asons
  • 84,923
  • 12
  • 110
  • 165
1

The pop() method removes the last element of an array, and returns that element. It changes the length of an array.

const popArray = function(array) {
  for (let x = 0; x <= array.length; x++) {
      console.log("value of x :", x);
      console.log("array.length :", array.length);
      array.pop();
      console.log("array after", x + 1, "pop :", array);
  }
};

popArray(["A", "B", "C"]);

In the above demo we can see that everytime pop() happens on an array. array.length will decrease and value of x will be increase. Hence, At certain point of time (after 2nd iteration) value of x will be greater then the length of an array.

Use Array.from() method :

let rev = [];
const reverseArray = function(array) {
  var destinationArray = Array.from(array);
  for (var i in Array.from(array)) {
    rev.push(array.pop());
    console.log(rev, array);
  }
};


reverseArray(["A", "B", "C"]);
Debug Diva
  • 26,058
  • 13
  • 70
  • 123
0

Try using while loop instead of for loop

const reverseArray = function(array){                                       
let rev = [];                                                             
while (array.length>0)                                                        
{                           
rev.push(array.pop());                                                       
}                                                  
return rev;                                                                  
};                                             
console.log(reverseArray(["A", "B", "C"]));
Azhar
  • 74
  • 8
0

I put two versions for you. reverse will reverse the array without modifying the original array. reverseModify will do the same, but emptying the original array.

Choose the version that suits you better

const arr = [1, 2, 3, 4];

function reverse(array) {
  let result = [];
  for (let i = array.length - 1; i >= 0; i--) {
    result.push(array[i]);
  }
  
  return result;
}

function reverseModify(array) {
  let result = [];
  while (array.length) {
    result.push(array.pop());
  }
  
  return result;
}

console.log(reverse(arr), arr);
console.log(reverseModify(arr), arr);
Oscar Paz
  • 18,084
  • 3
  • 27
  • 42
0

The problem is divides into two independent operations.

  1. iterate all elements, usually take the length and perform a loop

  2. pop a value and push that value. This is the mechanism to take the lastitem and push it to the end of the new array.

The part for iterating needs only the actual length and a count down (by pop) to zero and then exit the loop.

For a count up, you need two vvariables, one for the counter and one for the end or length of the array, which is a static value, take from the given array, which is later mutating.

function reverseArray(array) {
    var rev = [];
  
    while (array.length) {
        rev.push(array.pop());
    }
    return rev;
}

console.log(reverseArray(["A", "B", "C"]));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392