Javascript while loop with function as conditional - javascript

My understanding is that the contents of a while loop executes while the condition is true. While working off of an example from an awesome O'Riely book, I've come across this implementation of the while loop...
window.onload = function(){
var next, previous, rewind; // globals
(function(){
// Set private variables
var index = -1;
var data = ['eeny', 'meeny', 'miney', 'moe'];
var count = data.length;
next = function(){
if (index < count) {
index ++;
};
return data[index];
};
previous = function(){
if (index <= count){
index --;
}
return data[index];
};
rewind = function(){
index = -1;
};
})();
// console.log results of while loop calling next()...
var a;
rewind();
while(a = next()){
// do something here
console.log(a);
}
}
I guess I'm wondering why, in this code, the while loop is not resolving to true infinitely? The function, next() isn't returning false after var index stops incrementing (++), is it? Shouldn't the console just be outputting eeny, meeny, miney, moe, moe, moe, moe.....etc...
I know this has probably been asked in some form, but have done searching and can't find a question or answer that explains using while (a = function()) {// do something} and how this loop is stopping after one pass through the array.

About why while (a = next()) {/*do something*/} doesn't repeat infinitely, it's about being coerced to false that counts - arguments are converted to booleans before being tested by the while loop. Things that coerce to false include 0, -0, undefined, null, "", NaN, and of course false itself.
When you assign something, it returns the value of the assignment itself. For example, if you do something like this:
var a;
console.log(a = '1234567890abcdefghijklmnopqrstuvwxyz');
It will log 1234567890abcdefghijklmnopqrstuvwxyz.
When the next performs index++, this increments the counter for the element index in the data array. This means that it will look for the next element in the data array every time you run the next() function - if there are no more elements, it will return undefined and therefore end the loop.
For example, see this:
var index = 0;
data = ['a','b','c'];
data[index]; // 'a'
index++;
data[index]; // 'b'
index++;
data[index]; // 'c'
index++;
data[index]; // undefined - if passed this will coerce to false and end the loop
Boolean(data[index]); // false

if (index < count) {
index ++;
};
When index is count - 1, this will still change index to count, right? And count is data.length. So, it then does this:
return data[index];
Which becomes
return data[data.length];
Since the length of an array is out of bounds of the array (they are zero-based), it will give undefined.
while(a = next()){
will become
while(a = undefined){
Since undefined is a falsy value, the loop will not be entered.

No,
It is not going to be an infinite loop. The while loop is basically going through the array and outputting it and when it is at the end of the array it just returns false and quits the loop.
This is something like;
foreach(a as nextArray)
{
//output
}
Hope this helps.

Related

Alternating Integers in a bit string

I am working on a leetcode question that requires writing a formula that determines whether or not a bit string for a given number n has alternating integers.
I feel like my logic is solid, but I think there is something wrong with the way I define the bit string and loop through it. Can someone help me find why this code is not working? Thanks, I am self taught and this is my first stab at a javascript leetcode, so any advice is appreciated!
//first we need to create a variable that is a string of the bits for whatever number is given
//create variables current and previous and set them to null
//create for loop that loops through each digit in the bit, and sets "current" the number being looped over
//is that value = to previous (null on first loop)? no, continue loop and set previous to current
//run through loop again, changing current value
//is current = previous? if yes, result = false
//if no, continue loop
//define function
var hasAlternatingBits = function(n) {
let current = null; //set current to null so first loop iteration is always ture
let previous = null; //previous is null so it can be changed during loop
let result = true; //result is true until loop finds consecutive integers
var bitString = n.toString(2).split(', '); //turn string into an array that can be looped
//create for loop that loops entire string, or until it finds two consecutive integers
for (let i = 0; i < bitString.length; i++) {
//set value of current to number being looped over in string
current = i;
//if current doesnt equal previous,
if (current !== previous) {
previous = current; //set previous to current
} else //if current does equal previous
result = false; //change return to false
break; //end loop
}
return result;
};
console.log(hasAlternatingBits(5)) // should return true
console.log(hasAlternatingBits(7)) // should return false
The main problem was that you were assigning current to i so it took the value from 1 to the length of n
Note also that you can loop through string as you loop through array as i did in the following snippet
Note also that you break was outside the else part of the code.
In your code you can just return false if previous === current and then save some time
//define function
var hasAlternatingBits = function(n) {
let current = null; //set current to null so first loop iteration is always ture
let previous = null; //previous is null so it can be changed during loop
let result = true; //result is true until loop finds consecutive integers
let bitString = n.toString(2);//turn into string
//create for loop that loops entire string, or until it finds two consecutive integers
for (let i = 0; i < bitString.length; i++) {
//set value of current to number being looped over in string
current = bitString[i]; // <-- here
//if current doesnt equal previous,
if (current !== previous) {
previous = current;//set previous to current
}
else { //if current does equal previous
return false
}
}
return result;
};
console.log(hasAlternatingBits(5))
console.log(hasAlternatingBits(7))
This is my shot on this:
n == 0 || n.toString(2).split("").every((n, i) => n != i % 2)
Explanation:
The every function will iterate over the list and accumulate the true results. It has the optional parameter i that is the iterator index. The result of i % 2 itself alternates between 0 and 1 for increasing values of i.
var fn = n => n == 0 || n.toString(2).split("").every((n, i) => n != i % 2)
console.log(fn(5)) // true
console.log(fn(7)) // false
console.log(fn(11)) // false
console.log(fn(21)) // true
console.log(fn(0)) // true
console.log(fn(1)) // true

Looking for a function that takes three arguments (x, y, z) and checks if the type of all three is a string

Don't know what to put before the for loop. Don't know if I need an if/else statement. Trying to have it display in the console if items in an array are strings. So I know I need consol.log
var stringOne = isString('rob','bob','carl')
function isString() {
//I dont know what to put before for loop
for(let i = 0; i < arguments.length; i++) {
// Dont know if i need an if/else statement
// Trying to have it display in the console if items in an array are strings
// So I know I need consol.log
}
}
every would be appropriate here, and doing I/O (like console.log) is better left outside of the function. The name of the function suggests that it should return a boolean (true/false):
function isString(...args) {
return args.every(s => typeof s === "string");
}
console.log(isString('rob','bob','carl'));
Because it seems like you're a beginner, I will expand upon the code that you currently have, although #trincot did the best solution.
In a for loop, you can return a value so the loop won't continue. Because you only need to check if any of them are false, may it be in position 0, 1 or 2 in the array, you can return "false" immediately.
If there are only strings, the loop will continue until it ends, and then return "true" at the end of the method.
So you don't need any code before the for loop, only an if statement that returns "false" if any of the items in the array isn't a string.
var stringOne = isString('rob','bob','carl')
var stringTwo = isString('rob','bob', 1)
function isString() {
for(let i = 0; i < arguments.length; i++) {
if (typeof arguments[i] != 'string') {
return false
}
}
return true
}
console.log({stringOne});
console.log({stringTwo});
Something like this in the loop ought to do it:
this_arg = arguments[i];
if (typeof this_arg === 'string') console.log("arg number " + i + " is a string");
It shows how to do it a bit here
You may find every useful here. It iterates over the array you've made from the arguments and checks to see if each element matches the condition. If they all match it returns true, otherwise false.
function isString() {
return Array.from(arguments).every(str => {
return typeof str === 'string';
});
}
console.log(isString('rob','bob','carl'));
console.log(isString(2,'bob','carl'));

I have an issue with removing an object key with a for in loop

I'm using a for x in loop to check if a value is == to [] and if so remove the property using remove but it just does not seem to work.
const whosOnline = (a) => {
var obj = { online:[],
offline:[],
away:[] };
for(let i = 0; i < a.length; i++){
if(a[i].lastActivity > 10 && a[i].status == 'online'){obj.away.push(a[i].username)}
else if(a[i].status == 'offline'){obj.offline.push(a[i].username)}
else{obj.online.push(a[i].username)}
}
for(let x in obj){
console.log(obj[x])
if(obj[x] === []){delete obj[x]}}
return obj
}
you were close, however you also need to reference the array index for each object key value. Comments explaining this in the code below.
var obj = { online:[],
offline:[],
away:[] };
for(var x in obj){
if(!obj[x][0]){ // The 0 is the index inside the online array, next loop it will be the offline array and then the away array.
console.log('The array is empty');
// Do what you want to do now that it is empty
// This will continue to loop through and check the value of all the keys in the object.
}
}
console.log('done');
Good luck -
Mitch from
https://spangle.com.au
Using some debugging (simply testing if a console.log gets printed for instance) you find that your if-condition is never true.
This is because you test if an array equals a newly created empty array. This can never be the case, because objects are compared by object reference instead of value.
Instead you want to probably test to see if your array is empty by doing ‘if(obj[x].length===0)’ (or shorter: ‘if(!obj[x].length)’)

recursion return element of array

Just as a curiosity, Is it possible to return an element of an array during recursive iteration?
var index = 0;
function iterArray(arr){
if(arr && index <= arr.length ){
console.log(arr[index]); //Wanted to return this value instead calling any function here
index++
iterArray(arr)
}
}
NOTE :- Above code will not execute as I expect., But I want that it should work sort of arr.pop. Like:
k = ["a", "b", "c"];
ret1 = iterArray(k);
ret2 = iterArray(k);
ret3 = iterArray(k);
console.log(ret1, ret2, ret3)//"a", "b", "c"
Array.pop doesn't need recursive implementation.
But if it's general question - I'd think of some ECMAScript 6 generators.
function* iterArray(arr, idx) {
yield arr[idx];
yield* iterArray(arr, ++idx);
}
var arr = [1,2,3];
var g = iterArray (arr, 0);
console.log(g.next().value);
console.log(g.next().value);
console.log(g.next().value);
It's easy with generators, but to do similar things in ES5 environment - you'll need to transpile this code, or implement similar mechanism manually.
P.S: I used babel for transpilation.
This function may be use for searching exist key in array
function iterArray(arr){
if(arr instanceof Array && arr.length) {
return arr.shift();
};
}
k = ["a", "b", "c"];
ret1 = iterArray(k);
ret2 = iterArray(k);
ret3 = iterArray(k);
console.log('ret1 '+ ret1 + ' ret2 '+ ret2 + ' ret3 ' + ret3);
But, a function iterArray() changed a original array. Be carefully use this function
Function recurseOverArrayLookingForValue(array, value, currentIndex){
if (currentIndex == undefined)
{
currentIndex = 0;
}
if (currentIndex >= array.length)
{
Return -1; //not found
}
if (array[currentIndex] == value){
return currentIndex;
}
RecurseOverArrayLookingForValue(array,value,currentIndex);
}
That will recursively iterate over an array and return a value that matches the input. You call it without defining the currentIndex and it will start at the beginning and recurse to the end or you can define it and recurse from there.
The thing about recursion is you have to have a predefined exit state that will cause the function to terminate execution. Otherwise you get an infinite loop, basically. So in your example, you need to define some reason to return the value in that index or otherwise continue to recurse until you hit the end of the the array at which point you should, again, exit the function - this is why I choose to add the look for a value logic. If you're not looking for a specific value then you need to define another reason to exit returning a given value. Maybe call another function that returns true ilor false based on whatever you need the number for or if it meets certain criteria. Otherwise your exit status will always be the end of the array.
I don't see the need of recursion here, if you want to return a function then re-run the function you can use setTimeout depending on the framework, will work just fine in browsers.
var index = 0;
function iterArray(arr, index){
if(arr && index <= arr.length ){
index++;
setTimeout( fucntion(){iterArray(arr, index);^, 10 ); // Index is a global var, why you're passing it?
return (arr[index]);
}
}
This will continue the recursion but will return just the first match. To do what you're trying to accomplish by your example, the best way AFAIK is to mimic a class:
function nextIndexContainer( arrayToStore ){
// Make a copy so it doesn't get changed on the fly, or remove the JSON's if this behaviour is desireable
this.array = JSON.parse( JSON.stringify( arrayToStore ) );
this.index = 0;
this.next = function(){
if( this.array[ this.index ] )
return this.array[ this.index++ ];
else
return 'Last index reached';
}
}
To use it:
var arrIndex = new nextIndexContainer([1, 5, 8]);
console.log(arrIndex.next()); // '1'
console.log(arrIndex.next()); // '5'
console.log(arrIndex.next()); // '8'
console.log(arrIndex.next()); // 'Last index reached'

Emulate/use Continuations in JavaScript?

I have a function that computes product of numbers in an array. The function should work like this
function prod (array){
//compute and return product
}
var arr = [1,2,3,0,4,5,0,6,7,8,0,9];
the function call:
prod(arr); //should return 6
prod(arr); //should return 20
prod(arr); //should return 336 (6*7*8)
prod(arr); //should return 9
prod(arr); //should return 0
prod(arr); //should return 0
prod(arr); //should return 0
In scheme, this is done with continuations, by storing previous state of the function (state of the function is captured just before its exit point) see this
So, in short, I want the javascript function return different values at different times with same parameter passed everytime.
JavaScript is a well designed language, so I hope there must be something which can emulate this. If there happens to be nothing in JS to do it, I do not mind to conclude with failure and move on. So, feel free to say its impossible.
Thanks.
JavaScript is not capable of supporting continuations: it lacks tail-calls.
Generally I would write this to use a "queue" of sorts, although CPS is also do-able (just have a finite stack :-) Note that other state can also be captured in the closure, making it an "explicit continuation" of sorts ... in a very gross sense.
Example using a closure and a queue:
function prodFactory (array){
// dupe array first if needed, is mutated below.
// function parameters are always locally scoped.
array.unshift(undefined) // so array.shift can be at start
// also, perhaps more closured state
var otherState
// just return the real function, yippee!
return function prod () {
array.shift()
// do stuff ... e.g. loop array.shift() and multiply
// set otherState ... eat an apple or a cookie
return stuff
}
}
var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])
// array at "do stuff", at least until "do stuff" does more stuff
prod() // [1,2,3,0,4,5,0,6,7,8,0,9]
prod() // [2,3,0,4,5,0,6,7,8,0,9]
prod() // [3,0,4,5,0,6,7,8,0,9]
Happy coding.
"Finished implementation". Although this particular problem can avoid array mutation and just use an index: the same concepts apply. (Well, slightly different. With just an index the closed over variable would be altered, whereas with this approach an object is mutated.)
function prodFactory (array) {
array = array.slice(0)
return function prod () {
var p = 1
for (var n = array.shift(); n; n = array.shift()) {
p *= n
}
return p
}
}
var prod = prodFactory([1,2,3,0,4,5,0,6,7,8,0,9])
prod() // 6
prod() // 20
prod() // 336
You can give the function a property that will be remembered between calls:
function prod (array){
if (typeof prod.index === "undefined" || prod.currentArray != array) {
prod.currentArray = array;
prod.index = 0;
}
if (prod.index >= array.length)
return 0;
//compute and return product
var p = 1,
c;
while (prod.index < array.length) {
c = array[prod.index++];
if (c === 0)
return p;
p *= c;
}
return p;
}
I'm just guessing from your description of what should be returned that on an individual call to the function it should take the product of all of the numbers up to but not including the next zero or the end of the array. Calls after the end of the array should return 0? I may have the algorithm wrong for that, but you get the idea for what I'm suggesting to remember the function state between calls.
I've added a property to remember the current array being processed. As long as you keep passing the same array in to the function it will continue with the next elements, but if you pass a different array it will reset...
you can try something like
var index = 0;
function prod (array){
if(index < array.length){
var prod=1;
for(int i=index;i<array.length;i++){
if(array[i] != 0){
prod = prod * array[i];
}
else{
index = i+1;
return prod;
}
}
}
return 0;
}
this will update the global variable index everytime the function is called.
What you're looking for here are generators. As of 1.7, JavaScript supports them.

Categories

Resources