Reversing an array - Eloquent javascript chapter 4 - javascript

I am trying to work through this problem and I am having trouble understanding why the function reverseArrayInPlace isn't doing what I want it to. The console.log in the function will return a reversed array, but when I return the same thing, I don't get the reversed array.
I am a beginner so please dumb down the answers alot. Thanks
function reverseArray(array){
var x = [];
for (i=array.length - 1; i>=0; i--){
x.push(array[i]);
}
return x;
}
function reverseArrayInPlace(array){
console.log(reverseArray(array));
return reverseArray(array);
}
//console.log(reverseArray(["A", "B", "C"]));
// → ["C", "B", "A"];
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// → [5, 4, 3, 2, 1]
Edit: Thanks for the replies. Can I get some feedback on this function:
function reverseArrayInPlace(array){
for (i=1; i<array.length; i++){
var x = array[i];
array.splice(i,1);
array.unshift(x);
}
return array;
}
It seems to be working for me. Can you see anything wrong with it?

You aren't reversing the array in place
In the function you are creating a new array and then adding the elements in reverse order
in the first line of your function you create a new variable
var x = [];
Unless you want to change your implementation to handle both cases in reverseArray you need to implement a different solution in the reverseArrayInPlace function
You will need to
Determine the element to swap from the front
Determine the element to swap from the back
Know when you have reached the middle of the array and stop
You can optionally return the array if you would like to, but since the change is made in place you shouldn't need to

The reverseArrayInPlace() function isn't actually working in place. It is calling reverseArray() which is creating a new array to store the elements. As such, it needs to be returning this value.

why not just...
var arrayValue = [1, 2, 3, 4, 5];
arrayValue = arrayValue.reverse();
console.log(arrayValue);
edit: nvm i see you are workign through a tutorial. imo this is the most elegant way though.

Related

Why array value is changed only after first function?

I have a function that reverses an array:
function reverseArray(array) {
for(let i = 0; i <= Math.floor(array.length / 2); i++) {
let old = array[array.length - 1 - i];
array[array.length - 1 - i] = array[i];
array[i] = old;
}
return array;
}
and a function that creates an local scope array and push values in reversed order:
function reverseArr(arr) {
let output = [];
for(let i of arr) {
output.unshift(i);
}
arr = output;
return arr;
}
Suppose there is an element:
let arrayValue = [1, 2, 3, 4, 5];
If i invoke the first function with arrayValue as argument, arrayValue is changed:
reverseArray(arrayValue); // [5, 4, 3, 2, 1]
console.log(arrayValue); // [5, 4, 3, 2, 1]
However if i invoke the second function with arrayValue:
reverseArr(arrayValue); //[5, 4, 3, 2, 1]
console.log(arrayValue); //[1, 2, 3, 4, 5]
arrayValue is not changed even if i assigned the reversed value to the argument before return:
arr = output;
can someone explain me why?
thanks in advance.
Basically you take a new array with
let output = [];
and later you assign the new array to the parameter arr
arr = output;
Now you have two object references, one arr of the outer scope and a new one of output.
To overcome this, you need to keep the object reference of array. For getting a new content in an existing array, you could empty the array and push the values of the new array.
arr.length = 0;
arr.push(...output);
When you do arr = output in reverseArr you are referring to a new array. In other words arrayValue in the outer context and arr refer to two different objects.
You cannot change the value of the variable the function is called with. If you have a reference to the object you can mutate it, but you cannot make the outside variable refer to another object.
Basically, your first function reverses the list in-place (i.e. operates on the list itself directly, without building a new list), while the second function reverses the list out-of-place, by building a new list which contents are the reverse of the original list.
When working in-place, the changes you do to array inside the function are directly applied to arrayValue.
The reason why arr = output does not work the way you intend is pretty much what the other answers refer to. Namely, arr and arrayValue are two different references ("pointers"). Initially, both arrayValue and arr "point to" the same array when the function is called. However, arr = output makes arr point to the newly built list output instead.

Spinning the elements of an array clockwise in JS

I am supposed to rotate an array of integers clockwise in JS.
Here is my code for it:
function rotateArray(N, NArray)
{
//write your Logic here:
for(j=0;j<2;j++){
var temp=NArray[N-1];
for(i=0;i<N-1;i++){
NArray[i+1]=NArray[i];
}
NArray[0]=temp;
}
return NArray;
}
// INPUT [uncomment & modify if required]
var N = gets();
var NArray = new Array(N);
var temp = gets();
NArray = temp.split(' ').map(function(item) { return parseInt(item, 10);});
// OUTPUT [uncomment & modify if required]
console.log(rotateArray(N, NArray));
The code accepts an integer N which is the length of the array. The input is as follows:
4
1 2 3 4
The correct answer for this case is supposed to be
4 1 2 3
But my code returns
4 1 1 1
I cannot find where my code is going wrong. Please help me out.
All you need to do is move one item from the end of the array to the beginning. This is very simple to accomplish with .pop() (removes an item from the end of an array), then declare a new array with that element as the first:
function rotateArray(N, NArray) {
const lastItem = NArray.pop();
return [lastItem, ...NArray];
}
console.log(rotateArray(1, [1, 2, 3, 4]));
Doing anything else, like using nested loops, will make things more unnecessarily complicated (and buggy) than they need to be.
If you don't want to use spread syntax, you can use concat instead, to join the lastItem with the NArray:
function rotateArray(N, NArray) {
const lastItem = NArray.pop();
return [lastItem].concat(NArray);
}
console.log(rotateArray(1, [1, 2, 3, 4]));
If you aren't allowed to use .pop, then look up the last element of the array by accessing the array's [length - 1] property, and take all elements before the last element with .slice (which creates a sub portion of the array from two indicies - here, from indicies 0 to the next-to-last element):
function rotateArray(N, NArray) {
const lastItem = NArray[NArray.length - 1];
const firstItems = NArray.slice(0, NArray.length - 1);
return [lastItem].concat(firstItems);
}
console.log(rotateArray(1, [1, 2, 3, 4]));
function rotate(array,n){
Math.abs(n)>array.length?n=n%array.length:n;
if(n<0){
n=Math.abs(n)
return array.slice(n,array.length).concat(array.slice(0,n));
}else{
return array.slice(n-1,array.length).concat(array.slice(0,n-1));
}
}
console.log(rotate([1, 2, 3, 4, 5],-3));
The answer by #CertainPerformance is great but there's a simpler way to achieve this. Just combine pop with unshift.
let a = [1,2,3,4];
a?.length && a.unshift(a.pop());
console.log(a);
You need to check the length first so you don't end up with [undefined] if you start with an empty array.

How to change position of items within an array?

I am trying to create a function that changes positions of the items inside an array.
For example, I have this array:
[1, 2, 3] - if I call the function I get this => [2, 3, 1];
If I call the function again I should have this => [3, 1, 2];
The next time it should move again and so on.
But instead, if I call the function a second time I get this again => [2, 3, 1];
How could I make this work properly?
I know why this is happening, every time I call the function it receives the same values in the same order, I get that, that's why I built another array (move) to receive the values in the current order, it was an attempt to use this new array the second time I call the function, I tried it using an if/else statemente, but it didn't work; I also built another function passing 'move' as a parameter, and it worked, but just doesn't make any sense I have to have a different function for every move.
p.s. I am receiving the values from a HTML input.
<input id="numero1" value="">
<button onclick="moveLeft()">Move</button>
var n1 = document.getElementById("numero1");
function moveLeft() {
var str = n1.value;
var arr = str.split('');
var move = [];
console.log(arr);
arr.splice(0, 3, arr[1], arr[2], arr[0]);
console.log(arr);
for (var i = 0; i < arr.length; i++) {
move.push(arr[i]);
}
console.log(move);
teste(move);
}
function teste(move) {
console.log(move);
move.splice(0, 3, move[1], move[2], move[0]);
console.log(move);
}
If I understood properly, I think you can simplify in one line function:
const moveLeft = (arr) => arr.push(a.shift());
Notice that this function would mutate the content of the array but I guess that what you want:
const array = [1, 2, 3];
moveLeft(array);
console.log(array)// [2, 3, 1]
moveLeft(array);
console.log(array)// [3, 1, 2]
moveLeft(array);
console.log(array)// [1, 2, 3]
Basically with push we add a new element at the end of the array, and the new element we add, is the return value of the method shift, that removes the first element from an array and return it.
Edit
If you don't want to mutate the original array, the fastest way is create a shallow copy and pass that to the function:
const newArray = array.slice();
moveLeft(newArray);
console.log(newArray, array) // [2, 1, 3] [1, 2, 3]
However, a possible implementation that always return a new array, could be:
const moveLeft = (arr) => [...arr.slice(1), arr[0]]
Just for future reference. But keep in mind that here you're create a shallow copy, (so one new array) and returns a new array from the spread operator, so it's less ideal. It could be useful only if you're writing an API that never allow mutability.
Edit 2
As per comment, the input's value should reflect the new order, so, given the moveLeft function written before (the first one, that mutates the content):
<input id="numero1" value="">
<button onclick="inputMoveLeft('numero1')">Move</button>
And the JS:
function inputMoveLeft(id) {
const input = document.getElementById(id);
input.value = moveLeft(input.value.split("")).join("");
}
It should give you the result you were looking for.
pop and shift sound like the thing you need:
function rotateLeft(array){
let first = array.shift()
array.push(first); // add to end
return array;
}
function rotateRight(array){
let last = array.pop()
array.unshift(last); // add to front
return array;
}

When looping through values of a JS array, and I remove value, do I need to use while instead of for?

var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
for(var i = 0; i < myArray.length; i++){
if(isOdd(myArray[i])){
myArray.splice(i,1);
i--;
}
}
The code above takes an array of arbitrary length and checks each value. If the value of the bit of the array meets an arbitrary condition (in this case if it is odd), then it is removed from the array.
Array.prototype.splice() is used to remove the value from the array, and then i is decremented to account for the rest of the values in the array "moving down" to fill in the gap that the removed value left (so the loop doesn't skip over a value).
However, the for loop ends when i equals the length of the array, which gets shorter as values are removed.
Does the value of myArray.length decrease dynamically as the loop proceeds, or does it save the value at the start of the loop and not update as values are removed? If the latter, what can I do to fix my loop?
Thank you!
myArray.length is changing with the operation on the array. But looping and splicing leads to unwanted results, if not proper padded.
To prevent unnecessary corrections, use a while loop from the end, to keep the rest of the array for processing.
function isOdd(value) {
return value % 2;
}
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9],
i = myArray.length;
while (i--) {
if (isOdd(myArray[i])) {
myArray.splice(i, 1);
}
}
console.log(myArray);
The length property is read in every iteration, and the splice method does update its value, so it works as you would expect. However, I would say that this is not a good coding practice, a while loop is much more readable, so it should be the obvious choice.
To answer the question directly: you don't have to use while instead of for, but you definitely should.
Use Array.filter instead
var myArray = [1,2,3,4,5,6,7,8,9];
myArray=myArray.filter(function(item,index) {
return !(item % 2);
})
console.log(myArray)
This is where you'd want to use Array.prototype.filter() if you don't absolutely HAVE to modify the original array, in-place.
As you suspect, the .length property of the array is being updated every time you splice(). The filter() method was built for exactly this sort of operation.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
var filteredArray = myArray.filter(function(item){
return !isOdd(item);
});
console.log(filteredArray);
A more concise version of the above code:
var myArray = [1,2,3,4,5,6,7,8,9];
function isEven(value){
return value % 2 === 0;
}
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
An even more concise version relying on ES6 arrow syntax:
var myArray = [1,2,3,4,5,6,7,8,9];
var isEven = value => value % 2 === 0;
var filteredArray = myArray.filter(isEven);
console.log(filteredArray);
And, in the event that you absolutely MUST edit the array in-place / use splice() here, I would recommend using Array.prototype.forEach() over a for or while loop. forEach() is another higher order method that allows you to realize the same functionality with less boilerplate. As with most higher order methods/functions, it allows you to focus on defining what you need to do rather than exactly how it needs to be done.
var myArray = [1,2,3,4,5,6,7,8,9];
function isOdd(value){
return value % 2;
}
myArray.forEach(function(c, i, a){
if(isOdd(c)){
a.splice(i,1);
}
})
console.log(myArray);
You can use both of them and it's depends on which one you like. if you prefer to use while loop then Nina's answer looks good and if you want to use for loop then consider to manage counter changes by yourself completely or when the length changes:
function isOdd(value) {
return value % 2;
}
var arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr1.length;)
isOdd(arr1[i]) ? arr1.splice(i, 1) : i++;
console.log(arr1);
var arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < arr2.length; i++)
if (isOdd(arr2[i])) {
arr2.splice(i, 1);
i--;
}
console.log(arr2);

looping thorugh array of arrays (hashmap) and squaring numbers (using map function)

so here is my code (not working) what I want to do is loop through the hashmap and return a new hashmap with squared number so that any hashmap goes into function pow as an argument. and I want to do it in ES6 way.
var numbers = {};
numbers['two'] = [2, 4, 9];
numbers['one'] = [1, 2, 3];
function pow(arr){
for (var x in arr){
x.map(function(value)){
return value*value;
}
}
}
pow(numbers);
As Rocket commented above, when using a for...in loop, x is the index, not the actual value. You'll need to use arr[x] instead of simply x in your loop.
You can use arr.forEach() to iterate through an array's values in a more intuitive way, but there are compatibility considerations with that method, so your ability to use it may depend on where you're running the code. (More information here.) However, in this case, as Lucas pointed out in the comments, you're passing in an object, not an array, so forEach() will not work.
This does what you are looking for...as previously stated x in the for loop is the index. You would use arr[x] to get the array such as numbers['one']. You have to redefine the arr[x] with the new map you return. This does the trick. In ES6 you could destructure and then loop through. Feel free to ask for that if you'd like.
function pow(hm) {
for (var x in arr) {
hm[x] = hm[x].map(function(value) {
return value*value;
});
}
return hm;
}
ok I solved it `
var numbers = {};
numbers['one'] = [2, 4, 9];
numbers['two'] = [1, 6, 8];
function pow(arr) {
var arr1 = arr.map(function(value) {
return value*value;
});
return arr1;
}
console.log(pow(numbers['two']));
pow(numbers['two']);
` this is what I want - it's here http://jsbin.com/bicamimifi/edit?js,console,output

Categories

Resources