Google Console - Array Length discrepancy [duplicate] - javascript

This question already has answers here:
Is Chrome’s JavaScript console lazy about evaluating objects?
(7 answers)
Closed 4 years ago.
I'm currently in the process practicing using electron, but I'm quite new with javascript and I've come across a problem which has me completely baffled. I have the following code:
function getPaths() {
var dirPath = document.getElementById("mdir").innerHTML;
var filePaths = [];
fs.readdir(dirPath, function(err, dir) {
for(var i = 0, l = dir.length; i < l; i++) {
var filePath = dir[i];
filePaths.push(dirPath + "/" + filePath);
}
});
console.log(filePaths);
console.log(filePaths.length);
}
Which is supposed to look into a directory defined by dirPath, then it loops through and obtains the full path of all files in that directory. It appends them to an array, and then at the bottom, it logs the array to the console, followed by the length of the array.
What is baffling me is that given that code, the array logs to the console like expected, but then the console logs zero as the length. My current thinking is that it's got something to do with scope, but that doesn't make sense because I'm declaring the array, filePaths in the function above the one that's running. Unless I've missed something. Could anyone point out what I'm doing wrong?

readdir is asynchronous. It won't get the results right away. You should use the filePaths inside the callback. The only reason why the console shows the value is because the console evaluates the array when you unfold it.
When you press the little arrow on the left, put the mouse on the i box on the right. What happens is that the console keeps a reference to the array, so when the user unfolds the array it then shows the current value of the array. But when you log filePaths.length the array is empty because readdir didn't finish reading yet, that's why you get 0. But by the time you open the console and press that arrow, readdir will have already done reading and the console will print the current value of the array (after it's been filled).
Example to demonstrate the problem: (not a solution, it's just to understand what is really happening)
Open the browser console and try this code and see what happens:
var arr = [];
setTimeout(function() {
arr.push(1, 2, 3);
}, 5000);
console.log(arr.length);
console.log(arr);
Here the array and it's length are both logged before the array is filled. The array will be filled after 5 seconds. So the output will be 0 and a string representation of the array array[]. Now because arrays could have tons of data, the console won't show that data until the user unfolds the array. So what the console does is keep a reference to the array until the user press the unfold arrow. If you unfold the array before 5 seconds you'll see that the array is empty (not filled yet). If you wait until the 5 seconds pass then unfold it, then you'll see that it's filled, even though it was logged as an empty array.
Note: Also, the line that get logged to the console (something like > Array(0)) is just a string representation of the object/array at the moment the log happens. It won't get updated if the object/array changes. So that also may seem confusing sometimes.
I hope it's clear now.

Just to expand on #ibrahim-mahrir 's answer, they means like this
function getPaths() {
var dirPath = document.getElementById("mdir").innerHTML;
var filePaths = [];
fs.readdir(dirPath, function(err, dir) {
for (var i = 0, l = dir.length; i < l; i++) {
var filePath = dir[i];
filePaths.push(dirPath + "/" + filePath);
}
console.log(filePaths);
console.log(filePaths.length);
});
}

Related

Logging objects shows all properties but logging the properties individually shows 'undefined'

I'm trying to port a PHP function I built to Javascript and have been finding many differences that cause a lot of extra work. I am stuck on this one and cannot find any logic to it:
X: 95.29
Y: 27.39
testParse2.RXdec : 0.1
var curPos={};
curPos={};
console.log(curPos); //X:97.19 Y:27.39 (I expect an empty object)
console.log(curPos['X']); //undefined (seems ok but makes no sense with above)
console.log(curPos['Y']); //undefined (seems ok but makes no sense with above)
for(var Ri=0; Ri < 20; Ri++){
curPos['X'] = "";
curPos['Y'] = "";
console.log(curPos['X']); // "" (seems ok)
console.log(curPos['Y']); // "" (seems ok)
console.log(curPos); //X:97.19 Y:27.39
curPos.X = (((XY(lastPos[AV['A']], 'X')*1)+(testParse2.RXdec*1*Ri)).toFixed(10)*1);
curPos.Y = (((XY(lastPos[AV['B']], 'Y')*1)+(testParse2.RYdec*1*Ri)).toFixed(10)*1);
console.log(curPos); // X:97.19 Y:27.39 (I expect X:95.29 + 0.1 each loop Y:27.39)
console.log(curPos.X); // 95.29 (correct by why is above different?)
console.log(curPos.Y); // 27.39 (correct by why is above different?)
}
The things that confuse me the most are:
curPos gets a value before the loop even starts. The value is the
value that curPos should have after the final iteration.
during the loop the console log for curPos and curPos.X or .Y do not
contain the same values.
during the loop the console log for curPos is always the same despite changing .X and .Y each iteration
Edit: #str gave the correct explanation for the console trouble but it seems that this problem is beyond the console and actually effects the object values.
after using JSON.strigify I can see this (which is good):
console.log(JSON.stringify(testParse2));
"Xdec":97.99
"Xdec":98.09
"Xdec":98.19
but now I try to transfer the data to its final array but that final array is filled with 'lazy' values:
T['tool'][T['curTool']]['points'][lineID] = testParse2;
console.log(JSON.stringify(T));
"Xdec":98.19,"Ydec":27.39,"curX":323.19,"curY":177.39
"Xdec":98.19,"Ydec":27.39,"curX":323.19,"curY":177.39
"Xdec":98.19,"Ydec":27.39,"curX":323.19,"curY":177.39
If I stop using objects in the loop and switch to variables then build my final array like this it works:
T['tool'][T['curTool']]['points'][lineID] = {'curX' : curX,
'curY' : curY,
'TYP' : 'DR',
'lineID' : lineID,
'lineName' : lineName,};
How do you send the actual object values at a particular iteration of a loop to a different array?
Browsers evaluate objects lazily in when logging. So when you expand them after the loop, they will show the properties they have at the moment of expanding and not the ones they had when the object was logged.
You can verify that by using
console.log(JSON.stringify(curPos));
instead of
console.log(curPos);

Array has only 4 elements but it shows like it has 5 elements even after removing empty elements

I have an array with 4 elements and in one scenario I want to pop one element from the array. But after pop array gives the same result as before.
I have checked in the console window and there I have find a different behavior like array length is 4 and it shows like it has 5 elements.
I have tried to remove the empty elements also but still the same issue is coming .
var brudcrumbDataArray=JSON.parse(brudcrumbDataString);
brudcrumbDataArray = brudcrumbDataArray.filter(function(n){ return n != undefined });
console.log(brudcrumbDataArray)
brudcrumbDataArray.pop();
console.log(brudcrumbDataArray)
Her is the array :
[{"name":"Dashboard","url":"","path":"","class":"icon-home2 position-left","type":"MainMenu","queryParams":""},{"name":"Main","url":"#/dashboard","path":"/dashboard","class":"","type":"SubMenu","queryParams":""},{"name":"Sub Accounts","url":"#/account/customers","path":"/account/customers","class":"","type":"SubMenu","queryParams":""},{"name":"End Users","url":"#/account/endusers","path":"/account/endusers","class":"","type":"SubMenu","queryParams":""},{"name":"Profile","url":"#/user-dashboard","path":"/user-dashboard","class":"","type":"SubMenu","queryParams":""}]
After pop also array gives the same data and same length. Can someone help me to solve this issue ?
Snippet, check your console to see the problem:
var brudcrumbDataString =`[{"name":"Dashboard","url":"","path":"","class":"icon-home2 position-left","type":"MainMenu","queryParams":""},{"name":"Main","url":"#/dashboard","path":"/dashboard","class":"","type":"SubMenu","queryParams":""},{"name":"Sub Accounts","url":"#/account/customers","path":"/account/customers","class":"","type":"SubMenu","queryParams":""},{"name":"End Users","url":"#/account/endusers","path":"/account/endusers","class":"","type":"SubMenu","queryParams":""},{"name":"Profile","url":"#/user-dashboard","path":"/user-dashboard","class":"","type":"SubMenu","queryParams":""}]`;
var brudcrumbDataArray=JSON.parse(brudcrumbDataString);
brudcrumbDataArray = brudcrumbDataArray.filter(function(n){ return n != undefined });
console.log(brudcrumbDataArray)
brudcrumbDataArray.pop();
console.log(brudcrumbDataArray)
A result on the console seems to be correct. Both the logs on the console window are showing the result after the operation. Why is the first log showing a result in it? because, in javascript, complex objects get stored by reference, that's why your first log does show a result in it too.
Store by reference? what does it mean? : https://docstore.mik.ua/orelly/webprog/jscript/ch04_04.htm
Try below one,
var brudcrumbDataString =`[{"name":"Dashboard","url":"","path":"","class":"icon-home2 position-left","type":"MainMenu","queryParams":""},{"name":"Main","url":"#/dashboard","path":"/dashboard","class":"","type":"SubMenu","queryParams":""},{"name":"Sub Accounts","url":"#/account/customers","path":"/account/customers","class":"","type":"SubMenu","queryParams":""},{"name":"End Users","url":"#/account/endusers","path":"/account/endusers","class":"","type":"SubMenu","queryParams":""},{"name":"Profile","url":"#/user-dashboard","path":"/user-dashboard","class":"","type":"SubMenu","queryParams":""}]`;
var brudcrumbDataArray=JSON.parse(brudcrumbDataString);
brudcrumbDataArray = brudcrumbDataArray.filter(function(n){ return n != undefined });
console.log(JSON.parse(JSON.stringify(brudcrumbDataArray)))
brudcrumbDataArray.pop();
console.log(JSON.parse(JSON.stringify(brudcrumbDataArray)))
Look closely here, we are doing deep data copy with using JSON operations, by surrounding object in JSON.parse(JSON.stringify( )) and it won't point to reference anymore now.
And log will be more clear as below,
it showing because the reference to the same object so when you pop last element the object get modified and shows the length as 4 so the first consoled object also get updated.
var brudcrumbDataArray = [{"name":"Dashboard","url":"","path":"","class":"icon-home2 position-left","type":"MainMenu","queryParams":""},{"name":"Main","url":"#/dashboard","path":"/dashboard","class":"","type":"SubMenu","queryParams":""},{"name":"Sub Accounts","url":"#/account/customers","path":"/account/customers","class":"","type":"SubMenu","queryParams":""},{"name":"End Users","url":"#/account/endusers","path":"/account/endusers","class":"","type":"SubMenu","queryParams":""},{"name":"Profile","url":"#/user-dashboard","path":"/user-dashboard","class":"","type":"SubMenu","queryParams":""}]
//var brudcrumbDataArray=JSON.parse(brudcrumbDataString);
var newBrudcrumbDataArray = brudcrumbDataArray.filter(function(n){ return n != undefined });
console.log(brudcrumbDataArray)
newBrudcrumbDataArray.pop();
console.log(newBrudcrumbDataArray)

keeping localStorage objects that iterate whilst using clear() within the same function

I'm trying to clear all local storage when the user either completes the game loop or starts a new game, but also keep some values.
I can do this already with my sound values for volume:
// inside a conditional statement that fires when the user chooses to start a new game.
if (newGameBool === '1') {
var tst = myAu;
//myAu is the stored value that the user sets as sound using a range type input
localStorage.clear();
localStorage.setItem("Au", tst);//A newly cleared localStorage just got a new value, and it's the same as it was before.
UI.myLoad();//reload the function that uses LS to do things.
}
How do I do this for key's that have an iterating number attached to them?
Here is how I save them:
var i = +v + +1;
localStorage.setItem("v", i);
var vv = localStorage.getItem("v");
localStorage.setItem("LdrBrd_" + vv, JSON.stringify(LdrBrd));//saves all data with the iterating key name.
Calling them the way i did the sound function:
var gv = v + 1;//v calls the value from LS and adjusted for off-by-one error. gv is a local variable.
if (newGameBool === '1') {
var ldd, vg;
for (var ii = 0; ii < gv; ii++) {
var ld = localStorage.getItem("LdrBrd_" + ii);
if (ld != null) {
//these are the values that i want to pass beyond the clear point
ldd = JSON.parse(ld);//JSON string of data saved
vg = ii;//how many of them.
}
}
localStorage.clear();
for (var xx = 0; xx < vg; xx++) {
var nld = localStorage.getItem("LdrBrd_" + xx);
if (nld != null) {
localStorage.setItem("LdrBrd_" + ii, JSON.stringify(ldd));
}
}
localStorage.setItem("v", vg);
UI.myLoad();
}
I have been using console.log() in various spots to see what is going on. I comment-out the clear function just to see if the values were wrong and they don't save all all. I tried to make a fiddle, but the local storage wasn't working at all there. In visual studio, it works fine but the script to this file is almost 2000 lines long, so i tried to dress it up the best i knew how.
Thanks in advance for any help or guidance.
I was stuck on this for a few days, but i think i found something that will work, so i'll answer my own question in case there is value in posterity.
locatStorage.clear();
/* ^LS clear() function is above all new setItem codes, some variables are declared globally and some are declared at the top of the functional scope or as param^ */
var itemClass = document.querySelectorAll(".itemClass");//the strings are here
if (itemClass) {//make sure some exist
for (var p = 0; p < itemClass.length; p++) {//count them
mdd = JSON.parse(itemClass[p].innerText);//parse the data for saving
localStorage.setItem("v", v);//this is the LS item that saves the amount of items i have, it's declared at the top of the functions timeline.
localStorage.setItem("LdrBrd_" + p, JSON.stringify(mdd));//this setItem function will repeat and increment with 'p' and assign the right string back to the key name it had before.
}
}
The key is to keep the strings physically attached to an element, then call the class name. The i ran a loop counting them. 'mdd' will spit back each item i want. So then all that is left to do is re-set the item back to it's original status.
This has allowed me to create a way for my users to collect trophies and keep them even after clearing the localStorage when the he/she decides to start a new game.
I use CSS to hide the text from the string.
color:transparent;
In my gameLoop, i have a function that will read the saved strings and show them as cards just below the hidden strings.
Since you want to keep some values I recommend one of two things:
Don't call localStorage.clear() and instead only wipe out the values that you want using localStorage.removeItem('itemName'). Since you said the item names have a numeric component, maybe you can do this in a loop to reduce code.
Pull item(s) that you want saved first and restore them after calling clear(). This option is best if there are way more items that you want removed rather than saved (see below)
function mostlyClear() {
var saveMe = {};
saveMe['value1'] = localStorage.getItem('value1');
saveMe['anotherValue'] = localStorage.getItem('anotherValue');
localStorage.clear();
for(var prop in saveMe) {
if(!saveMe.hasOwnProperty(prop)) continue;
localStorage.setItem(prop, saveMe[prop]);
}
}

JavaScript stack, LIFO stack: value not as expected

I have the following code.
var stackMapIn = [];
var stackMapOut = [];
var stackBack = [];
stackMapOut.push("m1");
$scope.clickLinks = function(initialOut,initialIn,extra,backIn,backOut,name){
$('div#'+initialOut+'Map,'+extra).fadeOut('slow',function(){
$('.'+initialOut+'Details,.bkbtn'+backOut).css("display","none");
$('.'+initialIn+'Details,.bkbtn'+backIn).css("display","block");
$('.noQuery').css("display","none");
$("#buildingHeader").html(name);
$('div#'+initialIn+'Map').fadeIn('slow');
})
stackMapOut.push(initialIn);
stackMapIn.push(initialOut);
stackBack.push(backIn);
}
$scope.clickBack = function(bkbtnCheck){
alert(stackBack[0]);
mapOut = stackMapOut.pop();
mapIn = stackMapIn.pop();
stackBack.pop();
backIn = stackBack[0];
alert(backIn);
$('div#'+mapOut+'Map').fadeOut('slow',function(){
$('.'+ mapOut + 'Details,.bkbtn').css("display", "none");
$('.' + mapIn + 'Details,.bkbtn'+backIn).css("display", "block");
$(".noQuery").css("display","none");
$("#buildingHeader").html("Name");
$('div#' + mapIn + 'Map').fadeIn('slow');
})
}
Now I am going to do a quick run-through of what happens as this code runs.
The first time clickLinks runs:
initialIn = 'm2'
initialOut = 'm1'
backIn = 'Home'
clickBack has not run yet.
The second time clickLinks runs:
initialIn = 'm7'
initialOut = 'm2'
backIn = 'CentralPortfolio'
Ok so at this point things should be looking like this (I expect):
stackMapOut = ['m1','m2','m7']
stackMapIn = ['m1','m2']
stackback = ['Home','CentralPortfolio']
Now we run clickBack... Why does the alert output "Home"??
What I am trying to do here is, I have a series of things appearing and disappearing when clickLinks runs. Sometimes, the user can run clickBack in order to return to the previous state(status). So, I am using JavaScript stack to keep track of what state it is on and thus where it needs to return.
The problem is, I can run clickLinks once, clickBack once consecutively without issue. I can even run clickLinks a second time and still click the back button 2 times (to return to start) without issue. But I still don't understand why stackBack[0] (which should be the top of the stack ?) = "Home" at this point instead of "CentralPortfolio".
Because the real problem that I run into is now if I run clickLinks a third time: still stackBack[0] = 'Home' (when I expect it to be 'CentralCampus' at this point) and thus, it is the "Home" back button that is showing (while the correct other stuff is showing in accordance to mapOut and mapIn) instead of CentralPortfolio' back button to be showing; since I have 'CentralCampus' "popped" off before I use it.
Please, if any other information is needed or you need more clarification let me know. I tried my best to provide any needed information and make it as clear as possible.
Now we run clickBack... Why does the alert output "Home"??`
because stackBack[0] is Home. What would you expect it to do? You even have it in your question:
stackback = ['Home','CentralPortfolio']
The index 0 is the first element in your array, which is Home. The last element is stackBack[stackBack.length - 1]
using JavaScript stack
No, you are using a javascript array, but using it like a stack. Except when you index it like an array - which is the root of your confusion. You can use it as a stack with pop and push, but then don't try and index it with []
When you push on an array, you add an element to the end of the array. In other words, it ends up as the last element at the index yourArray.length - 1. When you pop, you take that last element off the array again. So popping stackback would give you back CentralPortfolio, and, of course, the first element is unchanged.

Javascript JSON Encode of associative array

I know there are several issues with JavaScript Array.push() method posted here, but I can't get the following example to work:
First of all, i got a global Array:
var results = new Array();
for (var i = 0; i < X.length; i++) {
results[i] = new Array();
}
Now this array should be filled on a button-event. All I want is to fill the Array with new Arrays and push some data to them. The following code should check if results[x][y] already is an Array (and create one if it's not) and push data to it.
So, in the end, there should be an Array (result) that contains X.length Arrays filled with an unknown number of new Arrays, each of them containing an unknown number of data:
function pushResult(result) {
if (typeof results[currentStim][currentDist] == 'undefined') {
results[currentStim][currentDist] = new Array();
}
results[currentStim][currentDist].push(result);
}
Problem is: It just doesn't work. I made sure that currentStim is never out of bounds, I made sure that the "if"-Statement is only accessed when needed (so the Array isn't overwritten with a new one) and I watched the return-value of push(), always throwring back a number representing the new array length. As expected, this number increases evertime a value is pushed to an Array.
However, when I finally call:
document.getElementById('results').value = JSON.stringify(results);
to pass results to my PHP-script, something like this will be passed:
[[],[[1]],[],[]]
push()was called MUCH more often than once (at least "1" is one of the results I wanted to be stored) and, as described, always returned an increasing arrayLength. How does that work? What happened to my data?
I testet this on Chrome as well as on Firefox, same result. It might be interesting that a seperate loop draws to a Canvas the same time, but that shouldn't interupt Array-Handling and onKey-Events, right?
Hope u can help me,
MA3o
EDIT:
pushResult is called like this:
// Handles Space-Events
function pushed(event) {
if (event.which == 32 && stimAlive) {
pushResult(1);
hitCurrent = true;
}
Where hitCurrent and stimAlive are just flags set somewhere else. Some code further the function pushedis registered as an event listener:
document.onkeydown = function(event) { pushed(event)}
All the functions are called correctly. Adding console.log(results) to every loop just shows the right Array, as far as I can see.
According to the comments, the problem might be that "currentDist" can be a float value.

Categories