Reverse Array without Reverse Method - Exercise from Eloquent Javascript Book - javascript

I am trying to reverse an array without using reverse method, but am encountering a behavior I do not understand when using pop and push functions together. Can someone explain why I am seeing the result I receive?
function reverseArray(array) {
let newArray = []
array.map((num)=> {
newArray.push(array.pop())
})
return newArray
}
console.log(reverseArray([1, 2, 3, 4, 5]))
Result is
// [object Array] (3)
[5,4,3]
This is driving me nuts, I'm wondering if it is something I'm not understanding with the return values of map, pop or push mutating the original array? This is an exercise from the book Eloquent Javascript, I have reversed an array in other ways before. I want to avoid using a loop if at all possible, so if there are any ideas for how to solve this that do not involve a loop it would be much appreciated.

When you call .pop(), you're mutating the value of array, removing the last element of the array. This prevents you from fully iterating over the entire array. Once you're trying to loop the 4th time, the length of the array is 2 ([1, 2]) which is less than 4 which we would need to access the 3rd index, so it stops iterating. You can either do this using a normal for loop or by copying the array.
function reverseArray(array) {
const newArray = new Array(array.length);
for (let i = 0; i < array.length; i++) {
newArray[array.length-1 - i] = array[i];
}
return newArray;
}
console.log(reverseArray([1, 2, 3, 4, 5]));
or (but this is really weird, don't do this)
function reverseArray(array) {
const copy = array.slice(0);
const newArray = [];
array.forEach((_) => {
newArray.push(copy.pop());
});
return newArray
}
console.log(reverseArray([1, 2, 3, 4, 5]));

I would try something like
while (array.length) {
newArray.push(array.pop());
}
There is no avoiding some kind of loop.

Perhaps, it's easier to just traverse the array from the end:
for (let i = array.length - 1; i >= 0; i--) {
nums.push(array[i]);
}
Or, if you want to use instance methods built in:
for (let i = 0; i < array.length; i++) {
nums.push(array.pop());
}
Alternatively, you could also do it in-place, by swapping the elements:
for (let i = 0; i < Math.floor(array.length / 2); i++) {
const tmp = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = tmp;
}

Related

getting weird result when trying to copy an array using slice() on VS Code

I'm doing the "Chunky Monkey" practice on freeCodeCamp and I'm trying to debug the program on VS Code. But I'm getting a different result than the one I get when I try to run it on the fCC console. fCC returns the expected result for the case, the original array,, but I get a different output on VS Code. Here's the code I made:
function chunkArrayInGroups(arr, size) {
let groupArr = arr.slice(0); // VS Code returns [4, 5, 6, 7]
//console.log(groupArr)
const groupTarget = Math.ceil(arr.length / size);
console.log(groupTarget)
let group = 1;
console.log(group)
let finalArr = [];
for (let i = 0; i < size; i++) { // iterates though the array 'size' times
if (group == groupTarget) { // if for when we are on the last array to fill
for (let j = 0; j < size; j++) // for to iterate size amount of times to see if there is no undefined
{
if (groupArr[j] == undefined) { //if there is a undifined value, we'll make a smaller array for the last group
finalArr.push(groupArr.splice(0, j))
console.log(finalArr)
}
}
}
finalArr.push(groupArr.splice(0, size)) // if we are not on the last group, we just splice the original value and push it
console.log(finalArr)
}
return finalArr; // once loops end, we return the finalArray
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 4);
Hope someone is able to help. Did I mess up something on VS Code? I only copied the exact code that I had.

Sort 2D array by matching the last values with the first one in the next index

So basically, I have this array
array = [[1,0],[2,1],[0,3],[3,2]]
Is there any quick method to convert the array to look like this
array = [[0,3],[3,2],[2,1],[1,0]]
What I want is for the first element of the new array to be always contain 0 in the first spot of the nested array. That's easy enough, because the sort() function does exactly that; the hard part is ordering the new array like the one above.
In the most simplest terms, I'd like for nested arrays to be "connected": see how the 3 of the first nested array matches the other 3 to its right and so on.
Feel free to leave any comments so I can try to explain the problem a little bit better.
For the example you gave the easiest solution would be to sort the 2d array by the second element of each inner array, as follow:
let array = [[1,0],[2,1],[0,3],[3,2]];
array.sort((a, b) => b[1] - a[1]);
In that way you can sort the array with the sort method according to the elements in the inner arrays.
let array = [[1,0],[2,1],[0,3],[3,2]];
array.sort((a, b) => b[1] - a[1]);
console.log(array);
You could take an object as reference and rebuild the array by taking the chained items.
const
getItems = (reference, value) => {
const a = reference[value];
return a ? [a, ...(a[1] === 0 ? [] : getItems(reference, a[1]))] : [];
},
array = [[1, 0], [2, 1], [0, 3], [3, 2]],
reference = array.reduce((r, a) => (r[a[0]] = a, r), {}),
result = getItems(reference, 0);
console.log(result);
Look at this method:
const array = [[1,0],[2,1],[0,3],[3,2]];
const result = [...array];
// Put the subarray with the zero element first
// You can use the sort() function but I guess this method
// performs better in terms of time
for (let i = 0; i < result.length; ++i) {
if (result[i][0] === 0) {
result.unshift(...result.splice(i, 1));
break;
}
}
// Now reorder the array so that the last index of a subarray
// matches with the first index of the other subarray
for (let i = 0; i < result.length; ++i) {
for (let j = i + 1; j < result.length; ++j) {
if (result[i][1] === result[j][0]) {
// recollocate the subarray so that it matches the way we want
result.splice(i + 1, 0, ...result.splice(j, 1));
break;
}
}
}
console.log(result)

Efficient algorithm for removing items from an array in place

I'm looking for an efficient JavaScript utility method that in O(n) will remove a set of items from an array in place. You can assume equality with the === operator will work correctly.
Here is an example signature (written in TypeScript for type clarity)
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) { ... }
My thought is to do this in two passes. The first pass gets the indexes that need to be removed. The second pass then compacts the array by copying backwards from the current index being removed through the next index being removed.
Does anyone have code like this already handy or a more efficient way to do this?
P.S. Please don't point out the filter function as that creates a copy of the array, it does not work in place.
Iterate over the array, copying elements that aren't in itemsToDelete to the next destination index in the array. When you delete an element, you don't increment this index.
Finally, reset the length of the array at the end to get rid of the remaining elements at the end.
function deleteItemsFromArray(array, itemsToDelete) {
let destIndex = 0;
array.forEach(e => {
if (!itemsToDelete.includes(e)) {
array[destIndex++] = e;
}
});
array.length = destIndex;
}
const array = [1, 2, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [3, 5]);
console.log(array);
function deleteItems(array,items){
let itemsToDelete=0;
let indexToSwap = array.length-1;
for(let i = array.length-1,currItem ; i>=0 ; i--){
if(items.includes(array[i]){
[array[i] , array[indexToSwap]] = [array[indexToSwap] , array[i]];
--indexToSwap;
++itemsToDelete;
}
}
array.splice(array.length-itemsToDelete,itemsToDelete);
}
This should work, I haven't tested it.
The idea is to swap the elements to delete to the end of the array. You can remove them at the end how I do in my code or you could use too the pop() function every time.
It's very, very simple - transform itemsToDelete to a Set. This ensures O(1) lookups. Then walk through the array backwards and remove items with Array#splice. In total, that gives you a linear O(n+m) time complexity:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: T[]) {
const setToDelete = new Set(itemsToDelete);
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = setToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
You can save the conversion step if you just make the function accept a set to begin with and change the signature to:
function deleteItemsFromArray<T>(array: T[], itemsToDelete: Set<T>)
function deleteItemsFromArray(array, itemsToDelete) {
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i];
const shouldBeDeleted = itemsToDelete.has(item);
if (shouldBeDeleted) {
array.splice(i, 1);
}
}
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
deleteItemsFromArray(
arr, new Set([1, 3, 5, 7, 8, 9])
)
console.log(arr);
Using copyWithin method will be helpful here.
method 1
Traverse from last index to first index, when ever we find the item to remove just copy/move elements to left.
Though we will be doing times the copy/move, But still have some unnecessary moving elements.
method 2
Traverse from first index to last index, when ever we find the item to remove, identify the group of elements to copy/move. This will avoid the unnecessary moving elements as in method 1.
function deleteItemsFromArray(array, delete_list) {
for (let i = array.length - 1; i > -1; i--) {
if (delete_list.includes(array[i])) {
array.copyWithin(i, i + 1).pop();
}
}
}
// Alternate way, with minimal copy/move group of elements.
function deleteItemsFromArrayAlternate(array, delete_list) {
let index = -1;
let count = 0;
for (let i = 0; i <= array.length; i++) {
if (delete_list.includes(array[i]) || !(i in array)) {
if (index > -1) {
array.copyWithin(index - count + 1, index + 1, i);
}
count += 1;
index = i;
}
}
array.length = array.length - delete_list.length;
}
const array = [9, 12, 3, 4, 5, 6, 7];
deleteItemsFromArray(array, [9, 6, 7]);
console.log(array);

How to write my reverse function properly?

i have a problem and i need help for this question.
My reverse function doesn't work the way I want it to.
function reverseArrayInPlace(array){
let old = array;
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
};
let arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
I expect on [5, 4, 3, 2, 1] but i have [5, 4, 3, 4, 5].
Why does it work this way? Please help me understand.
P.S I know about the reverse method.
Variable, with assigned object (array, which is a modified object in fact) - stores just a link to that object, but not the actual object. So, let old = array; here you just created a new link to the same array. Any changes with both variables will cause the change of array.
(demo)
let arr = [0,0,0,0,0];
let bubu = arr;
bubu[0] = 999
console.log( arr );
The simplest way to create an array clone:
function reverseArrayInPlace(array){
let old = array.slice(0); // <<<
for (let i = 0; i < old.length; i++){
array[i] = old[old.length - 1 - i];
};
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
P.s. just for fun:
function reverseArrayInPlace(array){
let len = array.length;
let half = (len / 2) ^ 0; // XOR with 0 <==> Math.trunc()
for( let i = 0; i < half; i++ ){
[ array[i], array[len - i-1] ] = [ array[len - i-1], array[i] ]
}
return array;
};
console.log( reverseArrayInPlace( [1, 2, 3, 4, 5] ) );
If you're writing a true in place algorithm, it's wasteful from both a speed and memory standpoint to make an unnecessary copy (as other answers point out--array and old are aliases in the original code).
A better approach is to iterate over half of the array, swapping each element with its length - 1 - i compliment. This has 1/4 of the iterations of the slice approach, is more intuitive and uses constant time memory.
const reverseArrayInPlace = a => {
for (let i = 0; i < a.length / 2; i++) {
[a[i], a[a.length-1-i]] = [a[a.length-1-i], a[i]];
}
};
const a = [1, 2, 3, 4, 5];
reverseArrayInPlace(a);
console.log(a);
Since this is a hot loop, making two array objects on the heap just to toss them out is inefficient, so if you're not transpiling this, you might want to use a traditional swap with a temporary variable.
function reverseArrayInPlace(a) {
for (var i = 0; i < a.length / 2; i++) {
var temp = a[i];
a[i] = a[a.length-i-1];
a[a.length-i-1] = temp;
}
};
var a = [1, 2, 3, 4];
reverseArrayInPlace(a);
console.log(a);
You have in old variable the same array (not by value, but by reference), so if you change any of them you'll have changes in both.
So you should create new array, make whatever you want with them and return it (or just copy values back to your origin array if you don't want to return anything from your function).
It happend like that because:
1) arr[0] = arr[4] (5,2,3,4,5)
2) arr[1] = arr[3] (5,4,3,4,5)
....
n) arr[n] = arr[0] (5,4,3,4,5)
So you can just make a copy (let old = array.slice()) and it'll be woking as you'd expect.

javascript unshift pop in a loop

I'm studying JS and I have this exercise that is asking to reverse an array in place (without the use of a second array) and without the use of 'reverse'. Although I already have the solution to the exercise I don't understand why my solution does not work, here it is:
function reverseArrayInPlace (arr){
const k = arr[0];
while (arr[arr.length-1] !== k){
arr.unshift(arr.pop());
}
return arr;
}
console.log(reverseArrayInPlace(arr1));
You take the end of the array and put it at the first position:
[1, 2, 3]
[3, 1, 2]
[2, 3, 1]
[1, 2, 3]
as you can see that actually doesnt reverse anything.
It will not work if your array contains duplicates of the first element.
As you are taking the first element as key, whenever any duplicate element becomes the last element, your loop exits.
Try this, just check if the two elements being selected is equal or not, if equal do not swap else swap. Iterate till the pointer k is <= the pointer j.
function reverseArrayInPlace (arr){
let first = 0;
let last = arr.length - 1;
let k = first, j = last;
while(k <= j){
if(arr[k] !== arr[j]){
let temp = arr[k];
arr[k] = arr[j];
arr[j] = temp;
}
k++;
j--;
}
return arr;
}
arr1 = [1, 2, 3, 4];
console.log(reverseArrayInPlace(arr1));
arr1 = [1, 2, 3];
console.log(reverseArrayInPlace(arr1));
This method will solve the problem without pop or unshift. Try this.
function reverseArray(array) {
for (let i = 0; i < Math.floor(array.length / 2); i++) {
let oldArray = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = oldArray;
}
return array;
}
console.log(reverseArray([1,2,3]));

Categories

Resources