Why does this 2d array create unexpected results? [duplicate] - javascript

This question already has answers here:
console.log() async or sync?
(3 answers)
Closed 6 years ago.
In the following code, I would expect a 2-dimensional array to be produced, pre-populated with zeros. The problem I am getting is that x[0][3] = 2 seems to be happening too quickly, so by the time of the console log inside the function, the array has a changed value already. I can't seem to remedy this, even with timeouts. What's going on?
function test(size) {
var row = [];
var return_me = [];
for (var i=0; i < size; i++) { row.push(0); }
for (var j=0; j < size; j++) { return_me.push(row.slice()); }
console.log("1:");
console.log(return_me);
return return_me;
}
var x = null;
console.log("0:");
console.log(x);
x = test(5);
console.log("2:");
console.log(x);
x[0][3] = 2;
console.log("3:");
console.log(x);
The unexpected result comes at the "1:" in the output, where I get:
0: 0
1: 0
2: 0
3: 2
4: 0

As Array is an object, you will see the actual (latest) value of that as console.log() shows a reference to the object and by the time you are opening it, the value has changed.
The option is to use console.dir() to print that current state of the object (although console.dir() does NOT work on chrome).
If you want to get the real value in chrome, print that one value, not whole object.
You can look at this post for expanded explanation and more info.

Related

Postfix operator in JavaScript [duplicate]

This question already has answers here:
++someVariable vs. someVariable++ in JavaScript
(7 answers)
Closed 4 years ago.
This is a basic JS question I think, but I just couldn't find an answer that I was satisfied with. I'm learning operators in JavaScript and I can't understand this following postfix example and how it works:
var x = 4;
var y = x++;
For example, when I alert x, it gives me 5. But when I alert y, it gives me 4. And I can't understand the logic behind it.
I understand that, because it's a postfix, x is assigned to y. And y reads just x, without the postfix. But why does then the original x read the postfix instead if I'm applying it to a different var?
If I just did var y = x + 1, the original x would stay unchanged. But that's not the case when I use postfix. Why would I even change the x with this method? Couldn't I just go then var x = 4; x++; ? And not bother changing it via another var?
I apologize if this is too basic and thanks in advance!
It's easier to think of the increment operators as little functions that return something. So x++ is a functions that increments x and returns the original value of x, whereas ++x does the same thing but returns the new value.
This comes in handy from time to time especially in loops where you want precise control of the stopping point. For example, compare:
let i = 0, j = 0;
while (++i < 5) {
console.log(i) // stops at 4
}
while (j++ < 5) {
console.log(j) // stops at 5
}
The difference is because the while loop evaluates one before the increment and the other after.
Similarly in recursive functions you'll often see things like:
function recurse(i) {
if (i == 5) return i
console.log(i)
return recurse(++i)
}
console.log("final: ", recurse(0))
But if you use the postfix return recurse(i++) you get infinite recursion.
Why would I even change the x with this method?
For some funny shortforms like:
const array = [1, 2, 3];
let i = 0;
while(i < array.length) console.log(array[ i++ ]);
Couldn't I just go then var x = 4; x++; ?
Yeah or just prefix, like ++x or just x += 1 which is more appropriate in most cases.

Weird behavior of javascript

Please see attached screenshot. See pendingApp property of Object. when I am debugging in eclipse then pendingApp show array of object, Which is correct! But when I am JSON.stringify(object) then showing me empty Array.
Please let me know reason of this behavior. I think I am not aware with any Java-Script thought/concept ?? :P :)
When I will save this Object into DB then blank array of pendingApp will be stored !!
var pending_app = [];
var new_record = {"pendingApp" : [], "installedApp" :[] };
....SOME CODE+conditions HERE....
pending_app[appId] = {'action' : action };
new_record.pendingApp = pending_app;
// create app-config data
return app_model.create(new_record); //will return promise object
It's not a weird behaviour but a common mistake of using an Array to store key-value data.
Short Answer : Use a literal Object to store these data
While you can add properties on every objects in Javascript, you cannot iterate over them with the default array mechanisms
for (var i = 0; i < array.length; i++){}
array.forEach();
Simple demonstration :
var array = [];
array["anId"] = 1;
array.length; // 0
array[4294967295] = 1; // Indice >= unsigned 32-bit Max Value
array.length; // 0
array[4294967295]; // 1
So JSON.stringify with the ECMAScript 5 Specification will use the Array mechanism to iterate over all items and will find nothing.
Unlike Objects that you can list properties with
Object.keys(array); // ["anId"]

Save a json object but not for reference [duplicate]

This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 8 years ago.
here is my problem, reported in a way much more simplified
this is the json (look at the json here if you want)
{"resource":[{"id":"1408694994","obj":[{"id":"1","action":[{"name":"ON","id":"301"},{"name":"OFF","id":"302"}]},{"id":"2","action":[{"name":"ON","id":"303"},{"name":"OFF","id":"304"}]}]},{"id":"1408694995","obj":[{"id":"3","action":[{"name":"ON","id":"305"},{"name":"OFF","id":"306"}]},{"id":"4","action":[{"name":"ON","id":"307"},{"name":"OFF","id":"308"}]}]}]}
Given an id (in this case we say id = 3), I must save the item with that ID.
And i save in a new object (newobj) the object with that ID, but changing the array ACTION in an OBJ with the only action ON
Example
<script>
var tt = '{"resource":[{"id":"1408694994","obj":[{"id":"1","action":[{"name":"ON","id":"301"},{"name":"OFF","id":"302"}]},{"id":"2","action":[{"name":"ON","id":"303"},{"name":"OFF","id":"304"}]}]},{"id":"1408694995","obj":[{"id":"3","action":[{"name":"ON","id":"305"},{"name":"OFF","id":"306"}]},{"id":"4","action":[{"name":"ON","id":"307"},{"name":"OFF","id":"308"}]}]}]}';
var myjson = JSON.parse(tt);
var search = 3;
console.log(myjson.resource[1].obj[0]);
for(var i = 0 ; i < myjson.resource.length; i++){
for(j = 0 ; j < myjson.resource[i].obj.length; j++){
if(parseInt(myjson.resource[i].obj[j].id) == search){
var newobj = myjson.resource[i].obj[j];
var obj_action = newobj.action;
for(var k = 0 ; k < obj_action.length ; k++){
if(obj_action[k].name == "ON"){
newobj.action = obj_action[k];
}
}
}
}
}
console.log(myjson.resource[1].obj[0]);
</script>
I can easily save in the variable newobj the object that i want. But why the initial json is changed ???
UPDATE
ok, I understand that it is clearly problem of how I save the obj in newobj variable. i dont' save the object, but only the reference at the object in json . how can I save it in the variable newobj ? (not by reference?)
One way of decoupling object references is to serialize and deserialize as JSON. In this case, you could do something like this:
var newobj = JSON.parse(JSON.stringify(myjson.resource[i].obj[j]));
Note that this won't work every time. Complex objects with functions or prototypes won't be saved, for example.
It also comes with a slight performance hit.

javascript closure ,who can help me to understand why

var functions = [];
(function foo() {
for (var i=0; i<5; i++ ) {
var f = function() {
console.log(i);
}
functions.push(f);
}
for (var i=0; i<5; i++ ) {
f = new Function("console.log(i);");
functions.push(f);
}
})();
for (var i=0; i<10; i++ ) {
functions[i]();
}
the output is :
5
5
5
5
5
5
6
7
8
9
can anybody help me sort out the flow?
first of all you should know whenever you want to run this function:
Function("console.log(i);");
this would use the current context's i variable as its value. so you might want to do:
var functions = [];
(function foo() {
for (var i = 0; i < 5; i++) {
var f = function func() {
console.log(func._i);
};
f._i = i;
functions.push(f);
}
for (var i = 0; i < 5; i++) {
f = new Function("console.log(" + i + ");");
functions.push(f);
}
})();
for (var i = 0; i < 10; i++) {
functions[i]();
}
I changed it this way and my result is:
0, 1, 2, 3, 4, 5, 6, 7 ,8, 9
Why is that?
1- all of the functions in your first for loop use a single variable i, and as its last value is 5 then all would print 5.
2- the other ones which get created in your second for loop, whenever they get invoked they would use the i variable in their current context, and as far as they get invoked in your last for loop, they use the i variable in the loop, to make it clear. you can easily change your last for loop like this:
for (var j = 0; j < 10; j++) {
functions[j]();
}
and as there is no i variable in the context (I changed it to j), then your output would be:
5 5 5 5 5 undefined undefined undefined undefined undefined
undefined is there because function can not fin any i variable in the context.
It's actually printing out five 5's and then 5, 6, 7, 8, 9. The first five 5's are from var i in that for loop, the functions save a reference to that var i and access it when you run it, and since it equals 5 at the end of the for loop, they all return 5. The second set of numbers comes from the quoted function accessing i from the for loop there, ergo it printing out 5, 6, 7, 8, 9.
Lots of good answers here:
How do JavaScript closures work?
The first 'for' loop is generating 5 functions each of them creates a closure which captures the 'i' as 5 (last value of 5 for that loop after last increment) so they all render out 5 (it just shows in Chrome as the first 5 with 5 in a circle next to it meaning the same value was logged out 5 times)
The second 'for' loop generates functions that will use 'i' from the scope it executes from, in this case the scope is the 3rd 'for' loop...
Closure allow you to access the variable from parent scope, which sometime cause strange or wrong behavior, here in this scenario closure is causing the output: 5 5 5 5 5 0 1 2 3 4
I am trying this code with:
f = new Function("alert("+i+");");
In first loop:
for (var i = 0; i < 5; i++) {
var f = function func() {
console.log(func._i);
};
f._i = i;
functions.push(f);
}
you are referencing i of parent means loop variable, so five different functions are created all with the same copy of variable i, which is changing with iteration, at the end of loop where condition break is i=5 since all functions are referencing the same copy, so change to original copy will effect all. So each function now has the i=5
Now in second loop, you are appending it as a string, which doesn't refer to original copy but creating a new string for each iteration:
for (var i = 0; i < 5; i++) {
f = new Function("console.log("+i+");");
functions.push(f);
}
this create five different functions with each different string:
console.log("0");
console.log("1");
console.log("2");
console.log("3");
console.log("4");
Now calling each function created by loop1 functions[0] to functions[4] is referring the same copy of i will print 5 while second functions[5]to end has there own strings to print.

var scope in javascript functions

I have the following small code snippet with the following expected and real output. My question is quiet simple. Why is it printing it this sequence? and how to I print the expected output?
Gr,
expected result:
0
1
2
0
1
2
real result:
0
1
2
3
3
3
this is the code:
var functions = [];
for (var i=0; i<10; i++) {
console.log (i);
functions.push (function () {
console.log (i);
});
};
for (var j=0; j<functions.length; j++) {
functions[j] ();
};
The functions that you push into the array doesn't log the value of i as it was when the function was created, they log the value of i at the time that the function is called.
Once the first loop ends, the value of i is 10, therefore any of the functions called after that will log the value 10.
If you want to preserve the value of i at different states, you can use a closure to make a copy of the value:
for (var i=0; i<10; i++) {
console.log (i);
(function(){
var copy = i;
functions.push (function () {
console.log (copy);
});
})();
};
The local variable copy will get the value of i and retain the value. You can also pass the value as a parameter to the function:
for (var i=0; i<10; i++) {
console.log (i);
(function(copy){
functions.push (function () {
console.log (copy);
});
})(i);
};
The expected result should be:
1
2
...
10
10
10
... 7 more times
The reason for this is simple. The console.log(i) inside your loop is correctly printing the value of i at each iteration of the loop. When you create and push a function into the functions array, what you're doing is closing each of those functions over the same variable i. At the end of your loop, i no longer satisfies your loop condition, so i = 10 is true. As a result, since each of those functions is going to execute console.log(i), and they're each closed over the same i, which now has value 10, you should expect to see the value 10 printed 10 times.
To prevent this, you will want to make a function which returns a function rather than creating functions directly in a loop:
var functions = [], i, j;
function createEmitter(i) {
return function () {
console.log(i);
};
}
for (i = 0; i < 10; i++) {
console.log(i);
functions.push(createEmitter(i));
};
for (j = 0; j < functions.length; j++) {
functions[j]();
};
Now, each of those created functions is closed over its own private scope variable, which resolves the problem.
You should update your code example to be i < 3 so that your results and function match up.
The functions you push into the functions array are storing a reference to the variable i, which after executing the top loop, is 10. So when it executes, it will go get the variable i (which is 10) and print that 10 times.
Here's a good way to see this in action:
for (var i=0; i<10; i++) {
console.log (i);
};
console.log(i) //=> 10
When you are using a variable, remember that the variable can change, it's not frozen at it's current value. You are only holding on to a reference to something else.
To fix this problem, I would run this type of minor refactor on the code (because the other answers have already created an extra scope, figured I'd give you something different). Rather than storing 10 functions, just store the numbers and execute them with a single function. This is a more elegant way to write it anyway, and takes up less space. I'm sure this example was abstracted from whatever code was really giving you the problem, but the general pattern still applies.
numbers = [];
for (var i=0; i<10; i++) {
console.log (i);
numbers.push(i);
};
numbers.forEach(function(i){
console.log(i);
});

Categories

Resources