I want to reverse an array that I input into a function.
But for some reason the console returns the first value of the array instead of taking the full array within the while loop so it can unshift the value at the end to the front then delete that same value.
function littleWHEW(lemonade) {
let i = lemonade.length - 1;
while (i >= 0) {
lemonade.unshift(lemonade[i])
lemonade.pop(lemonade[i])
i = i - 1
}
return lemonade
}
console.info(littleWHEW([1,2,3,4,5]))
Just use the
reverse()
method:
function littleWHEW(lemonade) {
return lemonade.reverse();
}
You should not add a parameter to the pop() method, by the way.
Since you asked for explanation, let's take a deep investigation. Before we do, please take a look at
unshift and pop
Firstly i = 4. Here're what happens in one loop:
[1,2,3,4,5] is the original array. lemonade.unshift(lemonade[i]) adds one element to the first position of lemonade. At this point i = 4 so lemonade[4] = 5, we have [5,1,2,3,4,5] (notice the bold).
Now you pop the last one out. [5,1,2,3,4,5] becomes [5,1,2,3,4].
You decrease i.
Now i = 3, And here're what happens in second loop:
[5,1,2,3,4] is the original array. lemonade.unshift(lemonade[i]) adds one element to the first position of lemonade. At this point i = 3 so lemonade[3] = 3, we have [3,5,1,2,3,4] (notice the bold).
Now you pop the last one out. [3,5,1,2,3,4] becomes [3,5,1,2,3].
You decrease i.
After one loop, your i does not point to the last element as expected, and makes things wrong (as second loop does).
Related
I'm learning Javascript, so pardon any mistakes in how I phrase the question.
I am writing a chess program to practice and learn. Currently, I am trying to write a function to find the color of a piece with the position as the parameter. The relevant pieces of code are as follows. The first two work as they were designed to, but the last does not.
let allPieces = board.getElementsByClassName('piece');
This sets allPieces as an object with the key values the html elemnts representing each piece, both black and white.
const getPiecePosition = function(element) {
let position = window.getComputedStyle(element).getPropertyValue('grid-row-start');
let letterIndex = alphabet.findIndex(function(letter) {
return letter === position[0];
});
letterIndex += 1;
return [letterIndex, Number(position[1])];
}
This takes a parameter in the form of the allPieces object with a specific key and returns the position as an array with the column number first and the row number second. ex. [2,3].
const getPieceByPosition = function(position) {
let pce = Object.keys(allPieces).forEach(function(piece) {
if (getPiecePosition(allPieces[piece]) == position) {
return allPieces[piece].borderColor;
}
})
return pce;
}
This is the function I am having trouble with. The idea behind it is that it will take each key in the allPieces object and loop through them using forEach() into the getPiecePosition() function to compare it with the position entered as the parameter. Since only one piece can inhabit any tile at once, it should never return multiple values.
I honestly don't know where to start debugging this code, but I have been trying for about an hour. It always just returns undefined instead of a truthy value of any kind.
Your last function has a few issues:
getPiecePosition(allPieces[piece]) == position
Assuming position is an array, you're trying to compare an array with an array here using ==. However, since the two arrays are different references in memory, this will always give false, even if they contain the same elements:
console.log([2, 3] == [2, 3]); // false
You're trying to return from the callback of .forEach(). This won't achieve what you want, as return will jump out of the .forEach callback function, not your outer getPieceByPosition() function. This leads me to your final issue:
The .forEach() method doesn't return anything. That is, it doesn't evaluate to a value once it is called. This means that let pce will always be undefined since you're trying to set it to the return value of .forEach(). This, in contrast to let letterIndex, is different, as letterIndex is set to the return value of .findIndex(), which does have a return value and is determined by the function you pass it.
One additional thing you can fix up is the use of Object.keys(allPieces). While this works, it's not the best approach for looping over your elements. Ideally, you would be able to do allPieces.forEach() to loop over all your elements. However, since allPieces is a HTMLCollection, you won't be able to do that. Instead, you can use a regular for loop or a for..of loop to loop over the values in your HTMLCollection.
Alternatively, there is a way to make allPieces.forEach() work.
Instead of using board.getElementsByClassName('piece');, you can use the method .querySelectorAll('.piece'), which will give you a NodeList. Unlike a HTMLCollection, a NodeList allows you to use .forEach() on it to loop through its elements.
The return type of getElementsByClassName HTMLCollection Object. You should't use Object.keys to loop through each of 'piece' element. Insted, use the follow.
for(var i = 0 ; i < allPieces.length ; i++){
var piece = allPieces[i];
... // and, do whatever with the getPiecePosition(piece)
}
While on the nodejs REPL I was trying to clean up an array defined as const array = [...] and just found out that using array.forEach(() => /pop|shift/()) would not work. After such expression the array will still hold values in it.
I'm well aware of better methods to clean the array, like array.splice(0), but I'm really curious about this behavior as seems counter-intuitive, at least for me.
Here's the test:
const a = [1, 2, 3]
a.forEach(() => {
a.shift()
})
console.log(a) // [ 3 ]
const b = [1, 2, 3]
b.forEach(() => {
b.pop()
})
console.log(b) // prints [ 1 ]
Notes
At first I was using arr.forEach(() => arr.pop()), so I though that one of the values was short-circuiting the forEach but wrapping the lambda in a body-block { .. } will also produce the same results.
The results are consistent across different node versions and browsers .. so it seems like it's well-defined behavior.
The quantity of leftover values, those still in the result array, change depending on the length of the input array, and seems to be Math.floor(array.length / 2)
The leftover values are always ordered accordingly to the /pop|shift/ method used, so some of the calls are actually changing the input array.
It also returns the same results by calling Array.prototype.forEach(array, fn)
Check out this quote from here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
If the values of existing elements of the array are changed, the
value passed to callback will be the value at the time forEach()
visits them; elements that are deleted before being visited are not
visited.
You're iterating from the beginning and removing the last element each iteration. This means you're moving forward 1, and reducing the length by 1, each iteration. Hence why you're ending up with floor(initialLength / 2) iterations. You're modifying the same array that you're forEaching, which as stated above means that you will not have the callback invoked for those pop'd elements.
Modifying an array while iterating over it is generally a bad idea. In fact, in Java, trying to do so would cause an exception to be thrown. But let's convert the forEach into an old-school for loop, and maybe you'll see the issue.
for (let i = 0; i < a.length; ++i) {
a.pop();
}
Is it clearer now what's going on? Each iteration you're shortening the length of the array by 1 when you pop the last element off. So the loop will end after iterating over half the elements -- because by then, it will have REMOVED half the elements, too, causing the value of i to be more than the current length of the array.
The same thing is happening when you use forEach: you're shortening the array with each iteration when you pop, causing the loop to terminate after only half the elements have been iterated. In other words, the iterator variable will move forward past the end of the array as the array shrinks.
.pop
Let's do this instead:
let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
console.log('before', x,i,a);
arr.pop();
console.log('after', x,i,a);
});
console.log(arr);
Your index is incrementing but your length is decrementing, so you're deleting the last elements when your index is in the first elements, thus the result where you delete the right half of the array.
.shift
Same: the iterating index goes one way, the length in another, so the whole things stop mid-work:
let arr = 'abcde'.split('');
arr.forEach((x,i,a) => {
console.log('before', x,i,a);
arr.shift();
console.log('after', x,i,a);
});
console.log(arr);
In another question of mine someone posted a really cool solution on how to flatten an nth nested array into one array. Since I did not want to start a long chat, and I still don't really fully understand what this code does, I thought I'd ask.
So my impression is that first in this case our array has length 2, and then it becomes 1 in the while loop. We then check is array[1], is an array. It is so we proceed. Now here is where I'm a bit confused. I believe we call the flatten function again so we can get into the nested arrays, but I'm still kind of hazy on the reasoning. We then take array[1] and slice it, here doesn't slicing just mean getting the whole array[l] anyways? since we go from 0th position to the end since slice() has no parameters.
function flatten(array) {
var l = array.length, temp;
while (l--) {
if (Array.isArray(array[l])) {
flatten(array[l]);
temp = array[l].slice();
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
}
}
}
var array = [['1', '2', '3'], ['4', '5', ['6'], ['7', '8']]];
flatten(array);
console.log(array);
https://jsfiddle.net/curw7mdp/
So I'm going to assume you understand the basics of recursion. I'll walk you through line by line.
var l = array.length, temp;
Declares l equal to the length of the array, and declares temp.
while (l--)
This decrements l after the iteration of the loop (as opposed to --l doing it before);
if (Array.isArray(array[l]))
This is checking if the 'l'th element in the array is another array. This is important because it means this element isn't flat.
flatten(array[l]);
This is where it gets fun, the function recursively calls itself so that it can now traverse the sub-array. And if the sub-array contains another array, it can keep going down deeper. I believe this is head recursion.
temp = array[l].slice();
Looks a little weird, but this allows us to extract the array into a new variable called temp.
temp.unshift(1);
temp.unshift(l);
[].splice.apply(array, temp);
This is also a very janky way of writing things, but basically it puts 1 and l as the first to elements in the temp array, and then it calls splice on array, with temp as the parameters. Only the first two elements of temp are passed as parameters (the two we put in just a second ago), so it uses basically removes the sub array, and replaces it with the flattened version.
References: splice(), apply(), unshift()
See comments for detailed explanation.
function flatten(array) {
// put the length of the array in l
// and create a temp variable with nothing in it
var l = array.length, temp;
// while(l--) will iterate through
// each item in the array backwards
// this is just a little faster than doing
// for(var i=0; i<array.length; i++)
while (l--) {
// The current item in the while loop is array[l]
// (that's an L, by the way, not a one)
// so this is saying "if current item is an array...."
if (Array.isArray(array[l])) {
// we call the function again (recursion)
// if this is an array. eventually, one of
// the inner items will be something other
// than an array and the recursion will stop
flatten(array[l]);
// when you call slice() with no parameters
// all it does is create a copy of the entire array
// and then we store it in temp
temp = array[l].slice();
// adds the number 1 to the begining of the array
// after we unshift one more time below,
// this will become the second parameter for the
// splice() call below, which is the number of
// items to delete
temp.unshift(1);
// adds the current index to the begining of the array
// this will be the first argument passed to splice()
// below, the first argument for splice is the
// index to start splicing..
temp.unshift(l);
// apply()'s first argument is context, in this case
// passing "array" as the context means the action will
// be performed on the array variable,
// which is the original arrray..
// apply()'s second argumetn is an array, each item
// of the array is passed in order to the function
// as arguments
// So basically this is saying.. "remove the current
// index (which is an array) and replace it with each
// of the items that were in that array
[].splice.apply(array, temp);
}
}
}
I've been staring at the answer to this question forever even writing down variables and whatnot through each iteration. I simply just don't get the process here. When I throw in console logs I see that permute is called input.length - 1 times before it ever gets to this line input.splice(i, 0, ch); It's hard to phrase the question when I am lost completely, but I guess some curiosities are: each time permute is called, it is a new instance of that function with it's own closure right? therefore variables changes that are within the function won't affect variables in other calls? does the function return permArr for each time it's called? and I suppose that doesn't necessarily affect the return of the first call? (my instinct tells me that the first time return happens, the function stops running).
Thanks for the insight.
Permutations in JavaScript?
var permArr = [],
usedChars = [];
function permute(input) {
var i, ch;
for (i = 0; i < input.length; i++) {
ch = input.splice(i, 1)[0];
usedChars.push(ch);
if (input.length == 0) {
permArr.push(usedChars.slice());
}
permute(input);
input.splice(i, 0, ch);
usedChars.pop();
}
return permArr
};
I'll give this a shot.
Overview
You start with two arrays that will have global scope: permArray will eventually hold all of the permutation arrays, and usedChars is a worker array used build each individual permutation array through all of the recursive calls. It's important to note that these are the only two variables that are accessible in the scope of every function created. All other variables have local scope to their own function call.
Then there is the recursive function which accepts an array as input and returns an array of array with all possible permutations of the input array. Now, in this particular function the recursive call is inside a loop. This is interesting because the terminating condition is actually more complicated than your basic recursive function--the recursive calls terminate when you pass in an empty input array and the for loop skips over the next recursive call.
Summary
Consider a four element array input. At a high level, the function is going to loop over the four elements of this array, pull out each element, and compute the permutation of that smaller array of three elements. With all of those three element permutations, it will append the original element pulled out to the beginning and add each of those four element arrays to permArray.
But, in order to find the permutation of the smaller three element arrays, we pull out each element, compute the permutation of that smaller array of two elements, add the element pulled out to the beginning of each of those permutations, and return each of those three element arrays up the recursion call stack so the original fourth element can be added to the beginning and counted as a permutation.
But, in order to find the permutation of the smaller two element arrays, we pull out each element, compute the permutation of that smaller array of one element, add the element pulled out the the beginning of each of those permutations, and return each of those two element arrays up the recursion call stack so the original third element can be added to the beginning of that permutation and returned up the stack.
But, in order to find the permutation of the smaller one element array, we pull out the element and compute the permutation of that empty array, which just returns, and we in turn just return our one element back up the stack so the original second element can be added to the beginning of that permutation and returned up the stack.
Details
Let's note some of the steps in this function:
var permArr = [],
usedChars = [];
function permute(input) {
var i, ch;
for (i = 0; i < input.length; i++) { // loop over all elements
ch = input.splice(i, 1)[0]; //1. pull out each element in turn
usedChars.push(ch); // push this element
if (input.length == 0) { //2. if input is empty, we pushed every element
permArr.push(usedChars.slice()); // so add it as a permutation
}
permute(input); //3. compute the permutation of the smaller array
input.splice(i, 0, ch); //4. add the original element to the beginning
// making input the same size as when we started
// but in a different order
usedChars.pop(); //5. remove the element we pushed
}
return permArr //return, but this only matters in the last call
};
Let's trace through the details using the array [4,3,2,1].
When it's first passed in, we'll take out the 4, push it to usedChars, remove it from input, and call permute on [3,2,1]. In this call, we'll push 3 to usedChars, remove it from input, and call permute on [2,1]. Then we push 2 to usedChars, remove it from input, and callpermuteon [1]. Then we push 1 tousedChars, and remove it frominput`.
This leaves us four calls deep and at step (2) with:
ch=1
input=[]
usedChars=[4,3,2,1]
At step (2), we're going to push our first permutation [4,3,2,1] to permArr. Then, moving on, since input is now empty the recursive call in (3) will simply return and in (4) we will simply add 1 back into input and remove 1 from usedChars--and this call returns.
So, at this point, we have started backing up our recursive calls and sit at step (4) with:
ch=2
input=[1]
usedChars=[4,3,2]
Step (4) will perform the critical step of the algorithm: the moving action. It takes ch=2 and adds it to the beginning of input and removes it from usedChars. This means that after step (5), we have:
ch=2
input=[2,1]
usedChars=[4,3]
Now take a look at where we are. We pushed [4,3,2,1] as a permutation, then backed up and swapped 2 and 1, and now we're going to go back into recursive calls to build [4,3,1,2] and add it as a permutation. After that we will back out some more, move around some more elements, and go back into the permutations and add them.
Getting back into it, after step (5) is executed we loop. That means we will push 1 to usedChars and make a recursive call with input=[2]. That call will push 2 into usedChars creating a the full array [4,3,1,2] and causing it to be added to permArray.
So, you're going to cycle up and down through the recursive calls building up a permutation, backing back out, rebuilding a different permutation, and backing out until you've looped over every possible combination.
I hope this helps!
The code is a bit hard to follow, because it's a mix of looping and recursion. It uses a global variable (usedChars) that is changed and restored during each call.
"each time permute is called, it is a new instance of that function
with it's own closure right? therefore variables changes that are
within the function won't affect variables in other calls?"
Well, yes and no. Each function call has its own scope, but as there are no variables that needs catching, there is no closure. The local variables i and ch and the parameter input are local to the scope, so each call has their own set of those variables. Any other variables are global, so they are shared between all calls.
The variable usedChars is changed in the code, and that change is visible to the code when the function does the recursive call, and then the variable is changed back to the previous state for the next iteration. When the function exists, the variable has the value that it had when the function was entered.
"does the function return permArr for each time it's called?"
Yes, but when the function calls itself, the return value is ignored. It's only when the array is returned from the outermost call that it is used.
Following a prior post on how to update the order of an array. I followed the suggestion of Michael Best and used splice() to modify the ordering of my array on button click
self.moveup = function (itemIndex) {
var i = self.itemList.indexOf(itemIndex);
if(i >= 1){
var array = self.itemList();
self.itemList.splice(i-1, 2, array[i], array[i-1]);
}
Where I am having trouble is in incrementing the items in the array. From reading the usage of Array Splice The first param indicates where the change should occur for moving up I would think that would be i+1 , the value 2 indicates how many items in the array would change so no change there and then the range I thought would be the selected item array[i] and the end would be [i+1] as I am increasing the position.
self.itemList.splice(i+1, 2, array[i], array[i+1]);
In the attached fiddler you can see the values increase but the items do not actually change order they are only replicating when you hit the down button. I expected the result to be the same as when calling moveUp.
I'd appreciate any pointers on what I am missing here. http://jsfiddle.net/rlcrews/SCWmk/5/
-cheers
Almost there. Here's how I did it.
When moving an item up, you need to swap it with the previous item. Thus, you need to replace the elements at indices i-1 and i with array[i] and array[i-1], respectively. Your moveup method does exactly that, so all is good.
Now, when moving an item down, you need to swap it with the next item. Thus, you replace the elements at indices i and i+1 with array[i+1] and array[i], respectively. Your code however changes the elements i+1 and i+2, which is no good. Instead, you should be doing:
self.itemList.splice(i, 2, array[i+1], array[i]);
You start splicing at i (as you're removing the elements at i and i+1) and you replace them (insert at that index) with array[i+1] and array[i].
On another note, your check for whether you can move the item down is incorrect. The only item you should not move down is the last item, i.e. the element at index self.itemList().length-1. Thus, the check should look like if (i < array.length - 1) { ... } (see the fiddle).