Why doesn't this function exit after the first iteration? - javascript

I do understand how the following function works in general. BUT why doesn't it exit after the first iteration (when there is a palindrome)? It checks the first character against the last in the if statement, which is true, what should (in my logic) execute the return statement... Thank you for any help explaining this! :)
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++)
debugger;
if (lowerCaseStr[i] === lowerCaseStr[lowerCaseStr.length - i - 1]){
return true;
}
return false;
}

It doesn't exit after the first iteration but after lowerCaseStr.length iterations because your code is equivalent to the code below
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++){
debugger;
}
if (lowerCaseStr[lowerCaseStr.length] === lowerCaseStr[-1]){
return true;
}
return false;
}
that is, it iterates lowerCaseStr.length; times but the only thing it does for each iterates is call debugger after that it tests to elements in the array that doesn't exists. (Both indices are out of bounds). That results in a comparison of two times undefined undefined === undefined which is always true.
As a side node if you return either true or false depending on a boolean expression then consider using one return statement instead:
return (lowerCaseStr[i] === lowerCaseStr[lowerCaseStr.length - i - 1]);

You need to switch the logic, check for inequality and return false. If you reach the end, return true.
function palindrome(str) {
var lowerCaseStr = str.toLowerCase();
for (var i = 0; i < lowerCaseStr.length; i++) {
debugger;
if (lowerCaseStr[i] !== lowerCaseStr[lowerCaseStr.length - i - 1]) {
return false;
}
}
return true;
}

Related

Google Apps javascript iterating through multiple arrays

I'm relatively new to customizing Google sheets and only learning javascript.
I'm trying to create a custom function that will search for a value in an array and return a string (something like vlookup).
What I'm trying to achieve is to get the value checked against multiple arrays. I wrote the below but for some reason, it checks only first element of the array (it did work but stopped for some reason and I cannot figure out why as I don't think I changed anything).
The second part will be trickier still, how to make it work against multiple arrays... I was thinking to create an array:
depots = [depot1,depot2...] and then change the code to "depots.length in for loop but even 1 array proves to be problematic.
var depot1 = ["device1", "device2", "device3"];
var depot1 = ["device1", "device2", "device3"];
function _depot(value) {
if (value) {
var depotCheckCase = value.toUpperCase();
for (var i = 0; i < depot1.length; i++) {
if (depotCheckCase == depot1[i]) {
return "Depot 1";
} else {
return false;
}
}
}
}
It only checks the first entry because you have return in both branches of your if/else, so no matter what, the first loop iteration will terminate the function.
Instead, move the return false; to the end, outside of the loop.
A couple of other issues:
You're declaring the same variable twice. Your second var depot1 = ... ends up being just an assignment (but since the array it's assigning has the same entries as the first one, you may not notice).
You're forcing the value to check to upper case, but not doing the same to the entry you're checking against.
Addressing all of those:
var depot1 = ["device1", "device2", "device3"];
function _depot(value) {
if (value) {
var depotCheckCase = value.toUpperCase();
for (var i = 0; i < depot1.length; i++) {
if (depotCheckCase == depot1[i].toUpperCase()) {
return "Depot 1";
}
}
return false;
}
}
console.log(_depot("device2")); // "Depot 1"
console.log(_depot("device8")); // false
Any idea how can I combine it with checking against second/third array?
You have two options:
Additional loops (simplest).
Finding the length of the longest array, using that as the loop max, and checking against undefined before comparing. Since [n] on an array when n is greater than or equal to the length will give you undefined, you can check that before doing the toUpperCase.
Here's that second one:
var depot1 = ["device1", "device2", "device3"];
var depot2 = ["device4", "device5"];
var depot3 = ["device6", "device7", "device8", "device9"];
function _depot(value) {
if (value) {
var depotCheckCase = value.toUpperCase();
var max = Math.max(depot1.length, depot2.length, depot3.length);
var entry;
for (var i = 0; i < max; i++) {
entry = depot1[i];
if (entry !== undefined && depotCheckCase === entry.toUpperCase()) {
return "Depot 1";
}
entry = depot2[i];
if (entry !== undefined && depotCheckCase === entry.toUpperCase()) {
return "Depot 2";
}
entry = depot3[i];
if (entry !== undefined && depotCheckCase === entry.toUpperCase()) {
return "Depot 3";
}
}
return false;
}
}
console.log(_depot("device2")); // "Depot 1"
console.log(_depot("device8")); // "Depot 3"
console.log(_depot("device5")); // "Depot 2"
console.log(_depot("device10")); // false
You could give yourself an array of arrays and do that in a loop rather than repeating the logic. I leave that as an exercise for the reader. :-)

Logical issue with Arrays and For loop JS

There is clearly something wrong with my logic or with the logic of JS (haha).
I really don't understand why one of them works and another one doesn't.
These functions are for checking if every single index in the array is the same. The first one works, and the second one doesn't and I don't see how the logic of these two are different (besides the obvious point of changing the positions).
1.
function isUniform(x) {
var first = x[0];
for(var i = 1; i < x.length; i++) {
if(first === x[i]) {
return true;
i++;
}
} return false;
};
2.
function isUniform(x) {
var first = x[0];
for(var i = 1; i < x.length; i++) {
if(x[i] !== first) {
return false;
i++;
}
} return true;
};
Arrays used :isUniform([1, 1, 1, 2]) and isUniform([1, 1, 1, 1])
Once you return inside a for-loop, the loop stops and the function ends.
In your first example, first will Never be equal to x[i], because you start i at 1 and first === x[0], so the loop will finish and return false.
In your second example, you'll always return false at i = 1, since x[1] !== x[0], so the loop will always return false after the first check.
Here is a breakdown as to how your functions work on a line-by-line level (I've included comments after each statement):
function isUniform(x) {
var first = x[0]; //SET "FIRST" to first element in array
for(var i = 1; i < x.length; i++) { //loop from second element to the end
if(first === x[i]) { //if "FIRST" is equal to this element
return true; //conclude that the ENTIRE ARRAY is uniform and quit function
i++; //incremenet "i" (note, the loop automatically does this, so this will result in an extra increment
}
} return false; //conclude the array is not uniform IF THE FIRST ITEM IS UNIQUE
};
Here is a breakdown of the second function:
function isUniform(x) {
var first = x[0];//SET "FIRST" to first element in array
for(var i = 1; i < x.length; i++) { //loop from second element to the end
if(x[i] !== first) { //if this element is not equal to the first CONCLUDE THAT THE ARRAY IS NOT UNIFORM and quit function
return false;
i++; //again, extra un-needed increment, but it technically does not matter in this case
}
} return true; //CONCLUDE that since no items were NOT equal to the first item, the array is uniform
};
Thus, it should now be clear that the second array fulfills your purpose while the first one does not. Really, the first one checks if any elements other than the first are equal to the first.

javascript for loop is not incrementing

I am trying to using a for loop for trying to validate the input of the user and this is the code i got.
function Valid() {
objfieldid = ["userMail", "userCont"]
objboxid = ["cancelMail", "cancelCont"]
return objfieldid.every(callnonvalid)
}
function callnonvalid(id) {
var valid = false
var objlength = objfieldid.length
objlength--;
for (var i = objlength; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i); //return 1, 1
return true
} else {
return false
}
}
}
As you can see, in the code, the for loop is running twice. but the i variable is left unchanged. Why would this happen?
btw, I did read different material about closure and i am sure there didnt got a closure problem
EDIT:guys please note that i did noticed the array is zero based, and i did minus the objlength by one.
Mistakes were found after checking the code carefully. The Mistake that I made was that I should not use the return for the out since that would stop the function from working, however that array.every Called the function twice which make the i not decreasing
I'm not sure why you're decrementing in your loop, because the performance gain would be infinitesimally small (it may even be slower, e.g. in Chrome/V8) and working in reverse order can get confusing further down the line, but that's your choice and I don't know enough about what you're doing to judge.
Either way, I don't think you'd want to decrement objlength before the loop begins as you are doing now. The whole point of a loop is to handle the incrementing/decrementing in the condition statement of the loop.
You would only decrement manually like that if you were going to move your if/else if/else statement into a closed over function and execute it recursively, decrementing the objlength from within the closure. Which would work, but it's unnecessarily complicated for what you're doing and you would gain nothing for rewriting the whole thing.
So, sticking with the looping approach, perhaps try either of these:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var objlength = objfieldid.length;
for(var i = 0; i < objlength; i++){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
or, if you want to decrement, use while instead of for:
function Valid() {
objfieldid = ["userMail", "userCont"];
objboxid = ["cancelMail", "cancelCont"];
return objfieldid.every(callnonvalid);
}
function callnonvalid(id) {
var valid = false;
var i = objfieldid.length;
while(i--){
var cobj = document.getElementById(objboxid[i]).checked;
if (document.getElementById(id).value != "" ){
var obj = document.getElementById(id).value;
} else if (cobj == true) {
alert(i);
return true;
} else {
return false;
}
}
}
Because the array objboxid[] has only two elements, the first time through your loop objboxid[2] will be attempting to fetch an array index that is out-of-bounds.
You probably meant something like:
for (var i = objlength; i > 0; i--){
var cobj = document.getElementById(objboxid[i-1]).checked;
or perhaps
for (var i = objlength-1; i >= 0; i--){
var cobj = document.getElementById(objboxid[i]).checked;

javascript: unreachable code in for iterator

Ok, im feeling kinda stupid with this one.
I am trying to define a function that checks wether an item is already present inside the listbox items collection (could be any collection actually)
here it is:
<script type="text/javascript">
function canInsert(listbox, item)
{
if (item == null)
return true;
var itemCount = listbox.GetItemCount();
for (var i = 0; i < itemCount; i++)
{
var nitem = listbox.GetItem(i);
if (nitem.value === item.value)
return false;
return true;
}
}
</script>
using VS 2015 CE, it says i++ is unreachable code. HOW AND WHY ?
Like Igor said, when you return true at the end of your for loop, JS will exit the function immediately returning the value 'true'.
You can execute the for loop you currently have, and if your if statement condition is met, false will be returned and the function will be exited. After your for loop, if the condition is never met, true will be returned. Your code should look like the below:
<script type="text/javascript">
function canInsert(listbox, item)
{
if (item == null)
return true;
var itemCount = listbox.GetItemCount();
for (var i = 0; i < itemCount; i++)
{
var nitem = listbox.GetItem(i);
if (nitem.value === item.value)
return false;
}
return true;
}
</script>

Check for continuous order in array in javascript

var userInput = prompt('enter number here');
var number = new Array(userInput.toString().split(''));
if (number ????){ //checks if the number is in a continuous stream
alert(correct);
}
else{
alert(invalid);
}
In Javascript, what can I do at "????" to check if it is in a continuous order/stream? Also how can I do this so that it only checks for this order/stream after a specific index in the array? Meaning the user enters say "12345678901234" which would pop up correct, but "12347678901234" would pop up invalid?(note there are two 7's) For the second part "3312345678901234" would pop up correct, how can this be implemented?
You can make a function that checks any string for a stream of continuous/increasing alpha-numeric characters starting at a given index like this:
function checkContinuous(str, startIndex) {
startindex = startIndex || 0;
if (str.length <= startIndex) {
return false;
}
var last = str.charCodeAt(startIndex);
for (var i = startIndex + 1; i < str.length; i++) {
++last;
if (str.charCodeAt(i) !== last) {
return false;
}
}
return true;
}
If it's numbers only and wrapping from 9 back to 0 is considered continuous, then it's a little more complicated like this:
function checkContinuous(str, startIndex) {
// make sure startIndex is set to zero if not passed in
startIndex = startIndex || 0;
// skip chars before startIndex
str = str.substr(startIndex);
// string must be at least 2 chars long and must be all numbers
if (str.length < 2 || !/^\d+$/.test(str)) {
return false;
}
// get first char code in string
var last = str.charCodeAt(0);
// for the rest of the string, compare to last code
for (var i = 1; i < str.length; i++) {
// increment last charCode so we can compare to sequence
if (last === 57) {
// if 9, wrap back to 0
last = 48;
} else {
// else just increment
++last;
}
// if we find one char out of sequence, then it's not continuous so return false
if (str.charCodeAt(i) !== last) {
return false;
}
}
// everything was continuous
return true;
}
Working demo: http://jsfiddle.net/jfriend00/rHH4B/
No need for arrays, just back though the string one character at a time.
When you hit a 0, substitute 10, and continue until the number
is not one more than the previous one.
function continuousFromChar(str, start){
start= start || 0;
var i= 0, L= str.length, prev;
while(L){
c= +(str.charAt(-- L)) || 10; // use 10 for 0
prev=+(str.charAt(L- 1));
if(c-prev !== 1) break;
}
return start>=L;
}
var s= "3312345678901234";
continuousFromChar(s,2)
/* returned value: (Boolean)
true
*/
This will do the checking in real-time entry, but a similar principle could be used to check an entry on a button submit or similar. I was not 100% sure as to which way you wanted it, so I went for the live method.
HTML
<input id="stream" type="text" />
Javascript
window.addEventListener("load", function () {
document.getElementById("stream").addEventListener("keyup", function (evt) {
var target = evt.target;
var value = target.value;
var prev;
var last;
var expect;
target.value = value.replace(/[^\d]/, "");
if (value.length > 1) {
prev = parseInt(value.slice(-2, -1), 10);
last = parseInt(value.slice(-1), 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
target.value = value.slice(0, value.length - 1);
}
}
}, false);
});
On jsfiddle
By changing the value here
if (value.length > 1) {
You can change where the checking starts.
Update: Ok, so it is function that you want, and you insist that it splits the string into an array. Then using the above as a reference, you could convert it to something like this.
Javascript
window.addEventListener("load", function () {
var testStrings = [
"0123456789012",
"0123456789",
"0123455555",
"555012345678901234",
"0123455555"];
function test(string, offset) {
if (typeof string !== "string" || /[^\d]/.test(string)) {
return false;
}
var array = string.split("");
var prev;
var last;
var expect;
return !array.some(function (digit, index) {
if (index >= offset) {
prev = parseInt(array[index - 1], 10);
last = parseInt(digit, 10);
expect = prev + 1;
if (expect > 9) {
expect = 0;
}
if (last !== expect) {
return true;
}
}
return false;
});
}
testStrings.forEach(function (string) {
console.log(string, test(string, 1));
});
});
On jsfiddle
As your question does not fully specify all possibilities, the above will return true for an empty string (""), of course you can simply add a check at the very beginning for that.
I also do not perform any checking for a valid number for your offset, but again this is something simple that you can add.
Of course these are just one (two) of many possible solutions, but hopefully it will set your mind in the right direction of thought.
There are some good answers here, but I would like to show a slight variation. I think it is important to showcase some different aspects of JavaScript and separating interests in code.
Functions as first class objects are cool - the exact rules for "continuous" can be changed with only changing the predicate function. Perhaps we should allow skipping numbers? No problem. Perhaps we allow hex digits? No problem. Just change the appropriate follows function for the specific rules.
This can be implemented generically because strings support indexing. This will work just as well over other array-like objects with an appropriate follows function. Note that there are no string-specific functions used in the continuous function.
Code also on jsfiddle:
// returns true only iff b "follows" a; this can be changed
function follows_1Through9WithWrappingTo0(b,a) {
if (b === "1" && a === undefined) {
// start of sequence
return true;
} else if (b === "0" && a === "9") {
// wrap
return true;
} else {
// or whatever
return (+b) === (+a) + 1;
}
}
function continuous(seq, accordingTo, from) {
// strings can be treated like arrays; this code really doesn't care
// and could work with arbitrary array-like objects
var i = from || 0;
if ((seq.length - i) < 1) {
return true;
}
var a = undefined;
var b = undefined;
for (; i < seq.length; i++) {
b = seq[i];
if (!accordingTo(b, a)) {
return false; // not continuous
}
a = b;
}
return true;
}
function assert(label, expr, value) {
if (!(expr === value)) {
alert("FAILED: " + label);
}
}
var follows = follows_1Through9WithWrappingTo0;
assert("empty1", continuous("", follows), true);
assert("empty2", continuous("foobar", follows, 6), true);
assert("skip", continuous("331234", follows, 2), true);
assert("good 1", continuous("123456789", follows), true);
assert("good 2", continuous("12345678901234", follows), true);
assert("bad seq 1", continuous("12347678901234", follows), false);
assert("bad seq 2", continuous("10", follows), false);
// here a different predicate ensures all the elements are the same
var areAllSame = function (b, a) {
return a === undefined || a === b;
};
assert("same", continuous("aaaaa", areAllSame), true);
Note that the skipping could also be extracted out of the continuous function: in a language with better "functional" collection support, such as C#, this is exactly what I'd do first.

Categories