-
• #2
Mon 2018.10.01
re: 'Could this be an error or do I miss something?'
Hello @user94507, your post is timely. I just experienced the same error within a nested for loop only an hour ago. e.g. two for loops. Break in same position just after the nested inside loop. You have saved me the effort of creating this original post.
1v99 on an authentic Pico
-
• #3
Give this a shot:
var oWifi = require('Wifi'); oWifi.scan(function(aAp) { aAp.sort(function(a, b) { var r = a.rssi === b.rssi ? 0 : (a.rssi < b.rssi ? 1 : -1); return r; }); console.log(aAp); var i=0, k, foundMy; while (!foundMy && i<aAp.length) { k = 0; // <---- restart k IN outer loop... only once outside is not enough while (!foundMy && k<aMyAp.length) { if (aAp[i].ssid === aMyAp[k].ssid) { // <---- compare [i] w/ [k] foundMy = aMyAp[k]; // ...and not [i] w/ [i] } k++; } i++; } if (foundMy) { oWifi.connect(foundMy.ssid, {password: foundMy.pwd}, function(err) { if (err===null) { console.log('Connection success to:', foundMy.ssid); } }); } else { console.log('no Ap found'); } });
...there were obviously also some other issues... - see
// <---- comments
- were there?What is the sort helping with? ...do you have duplicate ssid in scan result and want only the one to match w/ highest rssi value?
(Since labels are bad in general and worst in compiler free environment - as Espruino - in regard of nested control management, labels are not implemented in Espruino. Why though the simple, one-level break is not working... I don't know).
-
• #4
Actually I just tried this:
while (true) { while (true) { if (true) { break; } } if (true) break; }
And it breaks on 1v99 but works on cutting edge - so I'd just update your firmware. I will do a proper numbered release this week anyway hopefully.
With the new firmware you also have the option of using
Array.find
, butArray.filter
exists in the 1v99 build and might tidy this up nicely as well.Not tested, but you get the idea:
var matches = aAp.filter(function(ap) { return aMyAp.filter(myAp=>ap.ssid==myAp.ssid).length; }); if (matches.length) // connect with matches[0]
-
• #5
Thanks for your tips, you are right there are some other issues. I came not across this yet because I got this compilation error which was irritating me. The mentioned error message does not go away, even with the corrected code. Of course I can remove the the breaks, but the code is not efficient because it does not stop looping when the correct record is found.
-
• #6
@user94507, including
!foundMy
in both while-loop conditions leaves for sure the loops at the moment the very first match is found... no need for the breaks, even though they work... --- because I cannot find the tool/context that produces this error... are you using some preprocessor/xcompiler/transpiler etc? Your code - even though logically broken - is JS syntactically absolute correct...@Gordon, neat, it's like a find all, where @user94507 was looking for a find first. With the built-in
Array.filter()
I'm sure that with the expected array sizes it's still orders faster, and has the additional benefit of saving Espruino variables (code space in RAM). -
• #7
@allObjects ah ya! i have overseen this in your suggestion! Brilliant! The way around beak is perfect. Thank you.
@Gordon I have tried using the function Array.filter() replacing the inner loop. This works although I must pass a parameter to this function and so the Web Ide warns me "don't use a function within a loop".
By the way Array.find() is not implemented yet which would have been my favorite choice. -
• #8
@user94507, this should work too - notice the switching of inner w/ outer loop.
// findFirstInArrWForEach.js // @user94507 - 20181002 function findFirstIn(arr,matcher) { var r; arr.forEach(function(elt){ if (!r && matcher(elt)) r = elt; }); return r; } function findMyAp(aAp,aMyAp) { var myAp; findFirstIn(aAp,function(elt){ myAp = myAp || findFirstIn(aMyAp,function(elt2){ return elt.ssid == elt2.ssid; }); return myAp; }); return myAp; } var oWifi = require('Wifi'), myAp; oWifi.scan(function(aAp) { aAp.sort(function(a,b){return a.rssi===b.rssi?0:(a.rssi<b.rssi?1:-1);}); if ((myAp = findMyAp(aAp,aMyAp))) { oWifi.connect(myAp.ssid, {password: myAp.pwd}, function(err) { if (err===null) { console.log('Connection success to:', myAp.ssid); } else { console.log('Connect error: ',err); } }); } else { console.log('no Ap found'); } });
It still loops through all elements of the outer array (aMyAP[]), but stops comparing and looping through though the inner array (aAP[]) on first find (match).
Both the
matcher()
functions return true, but truthy is enough, and therefore it would be sufficient to justreturn my2 ;
If you do not like the looping through the first one and have even the optional telling where to start looping including handling of undefined and null arrays, you can use this
findFirstIn(arr,mchr,strt)
function (It was part of my survival kit w/ JS 1.2,..):findFirstIn(arr, mchr, strt, endx):
- arr is the array to search in
- mchr function taking array element as argument and return true on match, else false
- strt is optional search start index, defaults to 0 when ommitted
- endx is optional search end index exclusive, defaults to arr.length
returns first element for which 'mchr(element)' return true, else undefined
Note: does not help to find 'undefined' elements in array ;-)function findFirstIn(arr,mchr,strt,endx) { var i = ((strt) ? strt : 0)-1, m = ((arr) ? (endx) ? endx : arr.length : 0)-1; while (i<m) if (mchr(arr[++i])) return arr[i]; return undefined; }
- arr is the array to search in
-
• #9
And here is the test... (cannot stand code that has not been tested or at least has run once...).
// findFirstInArrWForEachTest.js // @user94507 - 20181002 var lg = false; // log to console var scanSetIdx = 1; // 1 finds, 0 does not find Ap in aMyAp function js(o) { return JSON.stringify(o); } function findFirstIn(arr,matcher) { var r; arr.forEach(function(elt){ if (lg) console.log("LX:",js(elt)); if (!r && matcher(elt)) r = elt; }); return r; } function findMyAp(aAp,aMyAp) { var myAp; findFirstIn(aAp,function(elt){ myAp = myAp || findFirstIn(aMyAp,function(elt2){ if (lg) console.log("LXX:",js(elt),js(elt2),elt.ssid == elt2.ssid); return elt.ssid == elt2.ssid; }); return myAp; }); return myAp; } // mocking aMyAp, oWifi w/ aAp scan result and test/validation var aMyAp = // test data for myAps as provided before scanning for Aps [ { ssid:"sWW", pwd:"pWW" } , { ssid:"sXX", pwd:"pXX" } , { ssid:"sYY", pwd:"pYY" } ]; var /* oWifi = require('Wifi'),*/ myAp; var oWifi = { aAp: // test data for aAp as scanned from oWifi [ [ { rssi:"rHB", ssid:"sNN" } // w/o myAp , { rssi:"rHC", ssid:"sQC" } , { rssi:"rUP", ssid:"sSS" } , { rssi:"rUQ", ssid:"sBQ" } ] , [ { rssi:"rAB", ssid:"sWW" } // w/ myAp , { rssi:"rAC", ssid:"sAC" } , { rssi:"rNP", ssid:"sXX" } , { rssi:"rNQ", ssid:"sPQ" } ] ] , scan: function(cb) { cb(this.aAp[scanSetIdx]); } , connect: function(ssid, options, cb) { var err = ((ssid == "sXX") && (options.password == "pXX")) ? null : "No matching ssid and password"; cb(err); } }; oWifi.scan(function(aAp) { if (lg) console.log("L1 ---(scanned):", aAp); aAp.sort(function(a,b){return a.rssi===b.rssi?0:(a.rssi<b.rssi?1:-1);}); if (lg) console.log("L2 ---(sorted):", aAp); if ((myAp = findMyAp(aAp,aMyAp))) { if (lg) console.log("L3 ---(found):",js(myAp)); oWifi.connect(myAp.ssid, {password: myAp.pwd}, function(err) { if (err===null) { console.log('Connection success to:', myAp.ssid); } else { console.log('Connect error: ',err); } }); } else { console.log('no Ap found'); } });
Code executes immediately on upload... since it is in level 0... not recommended (by me, especially not when it contains some async / callback and communication stuff, like connect to AP / Wifi)... all level 0 code - code that is executed on uploade / on reception by the Espruino board should always all go into an appInit() method that is called from the onInit() method. But in this (test code) case, it works... at least my (1v3 HW Version) PICO.
You can play with the
scanSetIdx
values 1 and 0, and of course with the 'test' data in mocked oWifi and in level 0 (aMyAp).If you want to track visually what is going on, set
lg = true;
to log in console:> ____ _ | __|___ ___ ___ _ _|_|___ ___ | __|_ -| . | _| | | | | . | |____|___| _|_| |___|_|_|_|___| |_| espruino.com 1v99 (c) 2018 G.Williams >L1 ---(scanned): [ { "rssi": "rAB", "ssid": "sWW" }, { "rssi": "rAC", "ssid": "sAC" }, { "rssi": "rNP", "ssid": "sXX" }, { "rssi": "rNQ", "ssid": "sPQ" } ] L2 ---(sorted): [ { "rssi": "rNQ", "ssid": "sPQ" }, { "rssi": "rNP", "ssid": "sXX" }, { "rssi": "rAC", "ssid": "sAC" }, { "rssi": "rAB", "ssid": "sWW" } ] LX: {"rssi":"rNQ","ssid":"sPQ"} LX: {"ssid":"sWW","pwd":"pWW"} LXX: {"rssi":"rNQ","ssid":"sPQ"} {"ssid":"sWW","pwd":"pWW"} false LX: {"ssid":"sXX","pwd":"pXX"} LXX: {"rssi":"rNQ","ssid":"sPQ"} {"ssid":"sXX","pwd":"pXX"} false LX: {"ssid":"sYY","pwd":"pYY"} LXX: {"rssi":"rNQ","ssid":"sPQ"} {"ssid":"sYY","pwd":"pYY"} false LX: {"rssi":"rNP","ssid":"sXX"} LX: {"ssid":"sWW","pwd":"pWW"} LXX: {"rssi":"rNP","ssid":"sXX"} {"ssid":"sWW","pwd":"pWW"} false LX: {"ssid":"sXX","pwd":"pXX"} LXX: {"rssi":"rNP","ssid":"sXX"} {"ssid":"sXX","pwd":"pXX"} true LX: {"ssid":"sYY","pwd":"pYY"} LX: {"rssi":"rAC","ssid":"sAC"} LX: {"rssi":"rAB","ssid":"sWW"} L3 ---(found): {"ssid":"sXX","pwd":"pXX"} Connection success to: sXX =undefined >
I have a nested while construct to find a value found in a source array to be within a destination array. When found want to break out from both loops. But it throws an error on the break statement in the outer loop. Could this be an error or do I miss something?
I am using Espruino 1v99 on a ESP2866
Here is the code: