This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
const array = [1, 2, 3, 4];
for (var index = 0; index < array.length; index++) {
setTimeout(() => {
console.log("I am at index " + index);
});
}
When I use the "var" keyword in the for loop I get the output
I am at index 4
I am at index 4
I am at index 4
I am at index 4
But When I use the "let" keyword in the for loop,
const array = [1, 2, 3, 4];
for (let index = 0; index < array.length; index++) {
setTimeout(() => {
console.log("I am at index " + index);
});
}
I am at index 0
I am at index 1
I am at index 2
I am at index 3
Can anyone tell me what's happening here?
The answer to your question requires you to understand
Hoisting in JS
Hoisting Diff in var and let type
Scope Diff in var and let type
Clousures
var type variable has a function/script scope and due to hoisting its declaration will move up out of the for loop, so it will come into the global scope and on every iteration, it is getting changed and all three closure will share the single variable i from the global lexical scope, whereas if we replace var with let then it will not move outside the for loop being a block-scoped, every iteration is a new block and there will be three different lexical scope for three timeout callback and a shared global scope(which has nothing from our code).
Related
This question already has answers here:
How to get value at a specific index of array In JavaScript?
(8 answers)
Closed 11 months ago.
I have a foreach loop for an array, with an index to tell me which loop i am on, and an item to tell me the value of the loop i am on. Probably an obvious answer, but I'd like to be able to get the item from the index. How might i do this?
const bar1beats = [1, 0.5, 0.5, 2]
bar1beats.forEach(foreachfunction);
function foreachfunction(item, index1){
checkitemof(index1 - 1)
}
The forEach function callback can accept three arguments - the item, the index, and the array itself.
function foreachfunction(item, index1, theArray){
if (index1 > 0) {
const previousItem = theArray[index1 - 1];
...
}
}
coinsToRender.slice(0, 2).forEach((coin, coinIndex) => {
coin.draw()
coin.name()
coin.update()
const dist = Math.hypot(coin.x - coin.y, coin.x - coin.y)
console.log(dist)
})
This is my code currently. Each rendered coin displays a ball inside a canvas. I want to be able to check the distances between each of those elements (balls) that are in an array. It's currently only between 2 for testing purposes but I will be removing that and reverting it back to 100 and eventually check the distances between all of them.
As you can see I currently am comparing one to itself. I had the idea to try to compare it to the coinIndex but that doesn't work. I've been staring at my code for hours; not sure what do it from here.
I don't know if the right thing to say is that I am trying to compare the distance of coin[0] and coin[1] because I want to compare the distances between all the coins eventually.
To approach such a problem it's always best to visualize it. In this case let's imagine an array of 4 coins:
coins= [1, 2, 3, 4];
Ultimately we want to check if any of those collide so basically we need to pick one and compare it's position with all the remaining elements. This needs to be repeated until we've reached the end of the array - 1.
First round: pick 1 -> compare with 2, 3 and 4
Second round: pick 2 -> don't compare with 1 and 2 (this happened in the first round) | compare with 3 and 4
Third round: pick 3 -> don't compare with 1, 2 and 3 (happened in first and second round) | compare with 4
This translates well to a nested for-loop - e.g.
let coins = [1, 2, 3, 4];
let firstCoin, secondCoin;
for (let a = 0; a < coins.length - 1; a++) {
firstCoin = coins[a];
for (let b = a + 1; b < coins.length; b++) {
secondCoin = coins[b];
console.log("compare ", firstCoin, " with " + secondCoin);
}
}
Edit
Nothing is stopping you from using a forEach instead of a plain for-loop for the outer loop. The only trouble is that you're looping over a segment of the array and not entirely:
coinsToRender.slice(0, 2)
For this use-case the forEach method offers an additional third parameter which returns the array being processed by forEach.
So the forEach variant of my example above would look a little like this:
let coinsToRender = [1, 2, 3, 4, 5];
coinsToRender.slice(0, 4).forEach((coin, coinIndex, arr) => {
for (let b = coinIndex + 1; b < arr.length; b++) {
console.log("compare ", coin, " with " + arr[b]);
}
});
This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
let keyword in the for loop
(3 answers)
Closed 2 years ago.
I am quite confused about the closing behaviour of functions declared inside of for loops, in particular about variables defined in initializers:
function createFunctions(){
const functions = []
for(let i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [0, 1, 2, 3, 4]
vs
function createFunctions(){
const functions = []
let i;
for(i = 0; i < 5; i++)
functions.push(() => i);
return functions;
}
const results = createFunctions().map(m => m())
// results: [5, 5, 5, 5, 5]
Since the anonymous arrow function declared in the for loop captures its scope, I would expect both cases to yield [5, 5, 5, 5, 5], since at call time, i's value is 5. The first result however seems to suggest that on each iteration through the loop, i is a different variable. However, if you repeat the test but the initialized variable is an object instead of a number:
function createFunctions(){
const functions = []
for(let obj = {}, i = 0; i < 5; i++)
functions.push(() => obj);
return functions;
}
const results = createFunctions().map(m => m())
// results: [{}, {}, {}, {}, {}]; results[0] === results[1]: true
we can see that all elements in the returned array are referentially equal, and so are not different variables. So it seems that the way that functions close over variables declared in the for loop initializer changes depending on whether the variable is a primitive or not, which sounds preposterous to me.
What am I missing?
So, I went through all the trouble of writing this question, and while researching I found the answer somewhat by accident, so I thought I'd switch this to a self-answer question so that if others encounter the same behaviour, it may be easier to find.
I found the answer here
In loops, you get a fresh binding for each iteration if you
let-declare a variable. The loops that allow you to do so are: for,
for-in and for-of.
Which explains why the objects are equal: we get a new variable which gets assigned to the value of the previous one at the end of the loop. In the case of the object, the new variable is pointing to the same object, which explains the referential equality. It also explains why the first case yields [0, 1, 2, 3, 4]: numbers are values, not references, unlike objects.
I was testing a few pieces of code out, and two statements that should return the same value return different values.
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
i = arr[i]
console.log(i)
}
returns [3, 4, 5], while
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(arr[i])
}
returns [3, 'foo', { bar: 'baz' }, false, 4, 5]
Why?
This is a great example of both the flexibility and pitfalls of imperative programming.
tl;dr you shouldn't be modifying i in you're for loops, and you also shouldn't be using for loops. A better way to iterate over the array is:
arr.forEach(function(item, i) {
console.log(item)
})
Imperative Programming
Copied from Wikipedia: In computer science, imperative programming is a programming paradigm that uses statements that change a program's state.
What does this mean?
In simple terms, whenever you are modifying variables (mutation), you're programming imperatively. For example:
var x = 1;
x = 5;
console.log(x + 5); // you would get 10
However, this can lead to unexpected problems which we can see in your example which is explained pretty well in other answers.
This is why in general, it's a good idea to avoid it (at least in Javascript) even though imperative programming is so convenient. This doesn't mean we always avoid it. Sometimes, programs need to run as efficient as possible, and imperatively modifying variables is the only way to achieve that.
In most cases, we don't need that efficiency if we're iterating (looping) through only a few hundred or even tens of thousands of variables. Our computers are fast enough that the difference in performance is not worth it.
Functional Programming
Functional programming is a great alternative. In practice, it boils down to one simple rule - no side effects. Side effects simply mean mutation, i.e modifying variables.
This avoids bugs that would be introduced in imperative programming since you know that once you initialize a variable, it will always be the same.
Below, I'll outline a pseudo introduction to functional programming and how it relates to your example. If you want to learn more about it, you can find countless resources online. Here's a pretty good article about functional programming with Javascript.
Some examples of functional programming
const x = 5; // we use const do denote x is constant and can't be changed ever
// x = 5 would give us an error!
const y = x + 5
// for (const i = 0; i < 10; ++i) is not functional since i is modified!
How should we loop without for loops? Recursion!
It's almost always good to avoid using for loops in the form of for (var i ...). This is because those loops are based on imperative programming, which we want to avoid if possible.
How do we print all the values of an array?
function printValues(arr, i = 0) {
if (i == arr.length) return; // exit when we reach the end
console.log(arr[i]); // let's call console.log
printValues(arr, i + 1); // do the same thing with the next index of the array
}
What if instead of just printing, we want to double every element in the array and double it? Instead of rewriting the same function, we can generalize this:
function forEach(arr, method, i = 0) {
if (i == arr.length) return;
method(arr[i], i)
return forEach(arr, method, i + 1)
}
// notice that forEach looks exactly like printValues, except now, we can pass in anything we would have passed into a normal for loop
forEach([1,2,3,4,5], function(item, i) {
const x = i * 2
console.log(x);
})
// now back to OP's example, we can use this forEach function
forEach(arr, function (item, i) {
console.log(item)
})
Of course, Javascript has these functions built-in and they are also optimized. So, you can just use the built-in forEach:
arr.forEach(function(item, i) {
console.log(item)
// or console.log(arr[i]);
})
There are other functions to use too other than forEach, these include:
map
filter
reduce
There are more, but these are the fundamental building blocks for iteration on arrays in functional Javascript and are must-haves in your toolkit!
I think second snippet explains itself only the first one needs explanation.
First of all there is no sense to write i = arr[i] inside a loop. It changes the looping number of the element at corresponding index. Which means anything unexpected could happen
What happened in your case is below
In first iteration i is 0 then its changed to 3 due to i = arr[i].
In second iteration i is 4 because last value of i is incremented due to i++. Due to i = arr[i] i becomes 4 the arr[4]
In third iteration i becomes 5 due to increment i++ and due i = arr[i] it remains 5.
In fourth iteration it becomes 6 and loop ends because i < arr.length becomes false
const arr = [3, 'foo', { bar: 'baz' }, false, 4, 5];
for(var i = 0; i < arr.length; i++) {
console.log(`Initial i = ${i}`)
i = arr[i];
console.log(`Final i = ${i}`);
console.log('\n')
}
Im just wondering who can explain the algorithm of this solution step by step. I dont know how hashmap works. Can you also give a basic examples using a hashmap for me to understand this algorithm. Thank you!
var twoSum = function(nums, target) {
let hash = {};
for(let i = 0; i < nums.length; i++) {
const n = nums[i];
if(hash[target - n] !== undefined) {
return [hash[target - n], i];
}
hash[n] = i;
}
return [];
}
Your code takes an array of numbers and a target number/sum. It then returns the indexes in the array for two numbers which add up to the target number/sum.
Consider an array of numbers such as [1, 2, 3] and a target of 5. Your task is to find the two numbers in this array which add to 5. One way you can approach this problem is by looping over each number in your array and asking yourself "Is there a number (which I have already seen in my array) which I can add to the current number to get my target sum?".
Well, if we loop over the example array of [1, 2, 3] we first start at index 0 with the number 1. Currently, there are no numbers which we have already seen that we can add with 1 to get our target of 5 as we haven't looped over any numbers yet.
So, so far, we have met the number 1, which was at index 0. This is stored in the hashmap (ie object) as {'1': 0}. Where the key is the number and the value (0) is the index it was seen at. The purpose of the object is to store the numbers we have seen and the indexes they appear at.
Next, the loop continues to index 1, with the current number being 2. We can now ask ourselves the question: Is there a number which I have already seen in my array that I can add to my current number of 2 to get the target sum of 5. The amount needed to add to the current number to get to the target can be obtained by doing target-currentNumber. In this case, we are currently on 2, so we need to add 3 to get to our target sum of 5. Using the hashmap/object, we can check if we have already seen the number 3. To do this, we can try and access the object 3 key by doing obj[target-currentNumber]. Currently, our object only has the key of '1', so when we try and access the 3 key you'll get undefined. This means we haven't seen the number 3 yet, so, as of now, there isn't anything we can add to 2 to get our target sum.
So now our object/hashmap looks like {'1': 0, '2': 1}, as we have seen the number 1 which was at index 0, and we have seen the number 2 which was at index 1.
Finally, we reach the last number in your array which is at index 2. Index 2 of the array holds the number 3. Now again, we ask ourselves the question: Is there a number we have already seen which we can add to 3 (our current number) to get the target sum?. The number we need to add to 3 to get our target number of 5 is 2 (obtained by doing target-currentNumber). We can now check our object to see if we have already seen a number 2 in the array. To do so we can use obj[target-currentNumber] to get the value stored at the key 2, which stores the index of 1. This means that the number 2 does exist in the array, and so we can add it to 3 to reach our target. Since the value was in the object, we can now return our findings. That being the index of where the seen number occurred, and the index of the current number.
In general, the object is used to keep track of all the previously seen numbers in your array and keep a value of the index at which the number was seen at.
Here is an example of running your code. It returns [1, 2], as the numbers at indexes 1 and 2 can be added together to give the target sum of 5:
const twoSum = function(nums, target) {
const hash = {}; // Stores seen numbers: {seenNumber: indexItOccurred}
for (let i = 0; i < nums.length; i++) { // loop through all numbers
const n = nums[i]; // grab the current number `n`.
if (hash[target - n] !== undefined) { // check if the number we need to add to `n` to reach our target has been seen:
return [hash[target - n], i]; // grab the index of the seen number, and the index of the current number
}
hash[n] = i; // update our hash to include the. number we just saw along with its index.
}
return []; // If no numbers add up to equal the `target`, we can return an empty array
}
console.log(twoSum([1, 2, 3], 5)); // [1, 2]
A solution like this might seem over-engineered. You might be wondering why you can't just look at one number in the array, and then look at all the other numbers and see if you come across a number that adds up to equal the target. A solution like that would work perfectly fine, however, it's not very efficient. If you had N numbers in your array, in the worst case (where no two numbers add up to equal your target) you would need to loop through all of these N numbers - that means you would do N iterations. However, for each iteration where you look at a singular number, you would then need to look at each other number using a inner loop. This would mean that for each iteration of your outer loop you would do N iterations of your inner loop. This would result in you doing N*N or N2 work (O(N2) work). Unlike this approach, the solution described in the first half of this answer only needs to do N iterations over the entire array. Using the object, we can find whether or not a number is in the object in constant (O(1)) time, which means that the total work for the above algorithm is only O(N).
For further information about how objects work, you can read about bracket notation and other property accessor methods here.
You may want to check out this method, it worked so well for me and I have written a lot of comments on it to help even a beginner understand better.
let nums = [2, 7, 11, 15];
let target = 9;
function twoSums(arr, t){
let num1;
//create the variable for the first number
let num2;
//create the variable for the second number
let index1;
//create the variable for the index of the first number
let index2;
//create the variable for the index of the second number
for(let i = 0; i < arr.length; i++){
//make a for loop to loop through the array elements
num1 = arr[i];
//assign the array iteration, i, value to the num1 variable
//eg: num1 = arr[0] which is 2
num2 = t - num1;
//get the difference between the target and the number in num1.
//eg: t(9) - num1(2) = 7;
if(arr.includes(num2)){
//check to see if the num2 number, 7, is contained in the array;
index1 = arr.indexOf(num2);
//if yes get the index of the num2 value, 7, from the array,
// eg: the index of 7 in the array is 1;
index2 = arr.indexOf(num1)
//get the index of the num1 value, which is 2, theindex of 2 in the array is 0;
}
}
return(`[${index1}, ${index2}]`);
//return the indexes in block parenthesis. You may choose to create an array and push the values into it, but consider space complexities.
}
console.log(twoSums(nums, target));
//call the function. Remeber we already declared the values at the top already.
//In my opinion, this method is best, it considers both time complexity and space complexityat its lowest value.
//Time complexity: 0(n)
function twoSum(numbers, target) {
for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] === target) {
return [numbers.indexOf(numbers[i]), numbers.lastIndexOf(numbers[j])];
}
}
}
}