This question already has answers here:
Javascript SetTimeout and Loops [duplicate]
(3 answers)
How do JavaScript closures work?
(86 answers)
Closed 8 years ago.
I know that this problem is related to JS scope and I have searched, but can't get the solutions from other stackoverflow questions to work.
I have this program
http://jsfiddle.net/0z525bhf/
function write(x, y) {
console.log(x);
console.log(y);
}
var data = {
"property": {
"1": {
"values": [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
},
"2": {
"values": [[11, 12], [13, 14], [15, 16], [17, 18], [19, 20]]
}
}
}
var delay = 1000;
for (var x in data.property) {
for (var i = 0; i < data.property[x].values.length; i++) {
var one = data.property[x].values[i][0];
var two = data.property[x].values[i][1];
setTimeout(function() {
write(one, two);
}, delay);
delay += 1000;
}
}
that reads data from an object and loops over both the object and the arrays in it. I want it to print the array values one second apart, but it always prints the values from the last iteration. I have tried with closures as suggested in the other question, but can't get it to work.
In the specific case of setTimeout, you don't even need a closure. You can pass variables to the timeout function, as follows:
for (var x in data.property) {
for (var i = 0; i < data.property[x].values.length; i++) {
var one = data.property[x].values[i][0];
var two = data.property[x].values[i][1];
setTimeout(function(one, two) {
write(one, two);
}, delay, one, two);
delay += 1000;
}
}
You could have applied you own research and used the following method.
for (var i = 0; i < data.property[x].values.length; i++) {
(function (one, two) {
setTimeout(function() {
write(one, two);
}, delay);
})(data.property[x].values[i][0], data.property[x].values[i][1]);
delay += 1000;
}
Working jsfiddle: http://jsfiddle.net/0z525bhf/1/
That's a problem with lexical closures : the function you pass to setTimeout closes over the variables one and two which get modified during iteration. This is why you should never declare a function in the body of a for loop.
Using a functional loop will solve it :
var delay = 1000;
for (var x in data.property) {
data.property.value[x].forEach(function (tuple) {
var one = tuple[0], two = tuple[1];
setTimeout(function() {
write(one, two);
}, delay);
delay += 1000;
});
}
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Javascript infamous Loop issue? [duplicate]
(5 answers)
Closed last month.
The following piece of code will produce 1, 2, 3, 4
const array = [1, 2, 3, 4];
for (let i = 0; i < array.length; i++) {
setTimeout(() => {
console.log(array[i]);
}, 1000);
}
Whereas using var i = 0 in the for loop will produce undefined, undefined, undefined, undefined
const array = [1, 2, 3, 4];
for (var i = 0; i < array.length; i++) {
setTimeout(() => {
console.log(array[i]);
}, 1000);
}
I understand var and let have different scopes but can somebody explain why in the var example, i evaluates to 4 in each iteration?
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
Here's my code. I wanna pull up even elements. But all I can pull up is 4 4 4 4 4.
function f6() {
let out = '';
let a6 = [[1, 2], [3, 4], [5, 6], [21, 34], [44, 56]];
for (let i = 0; i < a6.length; i++) {
for (let i = 0; i < a6[i].length; i++) {
if (a6[i][i] % 2 == 0) {
out += a6[i][i] + ' ';
}
}
}
console.log(out);
}
document.querySelector('button').onclick = f6;
<button>Push!</button>
Why?
You've used the same variable name, i, twice. Declaring let i inside your inner loop prevents you from accessing the let i from your outer loop. If you use a different variable name for iterating through each loop, e.g. j, then you should be fine.
If you only need to use the index for accessing values at that index, then you may instead want to try a for...of loop to avoid this problem altogether, e.g.:
const data = ... // 2D array
for (let row of data) {
for (let cell of row) {
// Use cell here
}
}
Your placeholder variable i is getting Shadowed by the second iteration, therefore you may want to use another placeholder such as j to have the correct reference to the element.
function f6() {
let out = '';
let a6 = [[1, 2], [3, 4], [5, 6], [21, 34], [44, 56]];
for (let i = 0; i < a6.length; i++) {
for (let j = 0; j < a6[j].length; j++) {
if (a6[i][j] % 2 == 0) {
out += a6[i][j] + ' ';
}
}
}
console.log(out);
}
document.querySelector('button').onclick = f6;
<button>Push!</button>
This question already has answers here:
Increment the name of variable
(2 answers)
Closed 3 years ago.
I'm learng some code with Javascript.
I would like to console.log Increment Variable such as a1,a2,a3,a4 and so on.
How can I implement it?
My only solution is eval() in a for loop:
console.log(eval('a'+i)).
However, it's not recommended in javascript.
var a1=10,a2=15,a3=20;
for(var i=1;i<=3;i++){
console.log(eval('a'+i));
}
I suggest you create an array to store all your a elements like so:
var arr = [10, 15, 20];
Which you can then loop over using a for loop. In the array the 0th index represents the a1 and the n-1th index represents an:
var arr = [10, 15, 20];
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
Another approach would be to use an object, where a1, a2, ... an are keys in your object:
var obj = {
'a1': 10,
'a2': 15,
'a3': 20
}
You can then use bracket notation to access your keys and values:
var obj = {
'a1': 10,
'a2': 15,
'a3': 20
}
for (var i = 1; i <= 3; i++) {
console.log(obj['a' + i]);
}
...or use a for...in loop to loop over your properties instead:
var obj = {
'a1': 10,
'a2': 15,
'a3': 20
}
for (var prop in obj) {
console.log(obj[prop]);
}
var a = [10, 15, 20];
for (var i=0; i<a.length; i++){
console.log("The value of a[i] is", a[i]);
}
In one of my projects I need to make a very long for loop that contains 9 different if/else statements. I'm trying to write cleaner, more maintainable, and readable code. I was hoping I could break this for loop down into a series of smaller functions.
I wasn't sure how to do this, so tried it and wrote a for loop in a function and a condition if else statement in another and tried calling it in the for loop.
This did not produce the desired result as i was undefined.
I understand this is because of the scope. My question is, is there a way to break down a for loop like this?
Here is the code:
let x = 5;
let y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const compare = () => {
if (x == y[i]) {
alert('hello');
}
}
const loop = () => {
for (let i = 0; i < y.length; i++) {
compare();
}
}
loop();
Simple pass 'i' as parameter to compare function,
let x = 5;
let y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const compare = (i) => {
if (x == y[i]) {
alert('hello');
}
}
const loop = () => {
for (let i = 0; i < y.length; i++) {
compare(i);
}
}
loop();
You can use forEach instead.
window.onload = () =>{
let x = 5;
let y = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const loop = ()=>{
y.forEach(item => {if(item===x) alert('hello')})
}
loop();
}
I have two arrays where I need to compare values and get the duplicates. I wrote most of the code but seem to be stumped on the comparison.
Here is my code:
function compare(arr1, arr2) {
for (var i = 0; i< arr1.length; i++) {
for (var j = 0; j < arr2.length; j++) {
if (arr1[i] == arr2[j]) {
console.log[i];
}
}
}
}
compare([5, 3, 2, 5, 1, 6], [6, 4, 2, 7, 10]);
I get the for loops to print all of the numbers, but for some reason the if statement comparison doesn't work. Is there something I am not getting about comparing values in arrays?
I am not looking for a straight up answer but guidance if possible.
Your code is quadratic in time since it iterates the second array for each item in the first array. A linear time solution is to convert the first array into a hash table, and then, for each item in the second one, instantly check if it is in the hash.
function intersect(a, b) {
var hash = {};
a.forEach(function(x) { hash[x] = 1 });
return b.filter(function(x) { return hash[x] === 1 });
}
c = intersect([5, 3, 2, 5, 1, 6], [6, 4, 2, 7, 10]);
document.write(c)
Do note, however, that this only works if items to compare are primitives, you cannot put objects in a hash, so the code has to be quadratic:
function intersect(a, b) {
return a.filter(function(x) {
return b.indexOf(x) >= 0
});
}
a = {x:'a'};
b = {x:'b'};
c = {x:'c'};
d = {x:'d'};
i = intersect([a,b,c], [a,b,d]);
document.write(JSON.stringify(i));
Regarding your bit about improving your current code, I suggest that you make your javascript more idiomatic, in particular,
get used to iteration methods instead of for loops
check the repertoire of built-in functions and use them wherever possible
and, for sanity's sake, never ever use ==