Javascript function to compare two versions - javascript

Im writting a function to compare 2 versions, and return true if the second is bigger then first version.
but my algorithm have a "hole", and I cant figure out how fix.
function compareversion(version1,version2){
var result=false;
if(typeof version1!=='object'){ version1=version1.toString().split('.'); }
if(typeof version2!=='object'){ version2=version2.toString().split('.'); }
for(var i=0;i<(Math.max(version1.length,version2.length));i++){
if(version1[i]==undefined){ version1[i]=0; }
if(version2[i]==undefined){ version2[i]=0; }
if(version1[i]<version2[i]){
result=true;
break;
}
}
return(result);
}
this exemples return as expected
compareversion('1','1') //false
compareversion('1','1.0.0') //false
compareversion('2.0','1.0.0') //false
compareversion('1.1','1.2') //true
compareversion('1','1.0.0.1') //true
but this
compareversion('1.1.0','1.0.1') //return true but should be false

This should work:
function compareversion(version1,version2){
var result=false;
if(typeof version1!=='object'){ version1=version1.toString().split('.'); }
if(typeof version2!=='object'){ version2=version2.toString().split('.'); }
for(var i=0;i<(Math.max(version1.length,version2.length));i++){
if(version1[i]==undefined){ version1[i]=0; }
if(version2[i]==undefined){ version2[i]=0; }
if(Number(version1[i])<Number(version2[i])){
result=true;
break;
}
if(version1[i]!=version2[i]){
break;
}
}
return(result);
}
The reason compareversion('1.1.0','1.0.1') fails is that your code first compares 1 to 1, then 1 to 0 (it does not break here since it only breaks if version1[i] < version2[i]) and then 0 to 1.
Since 0 < 1, it returns false.

If version1 is at that index bigger, you know it should return false. You only need to continue if they are equal.
if(version1[i]<version2[i]){
result=true;
break;
}
if(version1[i]>version2[i]){
result=false;
break;
}
// Will only get here if both are equal, in all other
// cases you broke out of the loop. So only continue
// checking the next index when this one was equal.

Your problem is that if you have 1.1.0 and 1.0.1,
the function does not reach the break when you need it to
if(version1[i]<version2[i]){
result=true;
break;
}
it keeps comparing the numbers until the end.
you get 1<1 == false, the if does not run
then you get 1<0 == false, the if does not run
and then 0<1 == true, if makes result = true.
you would need:
if(version1[i]<version2[i]){
return true;
}else
if(version1[i]>version2[i]){
return false;
}

None of these answers is optimal for the following reasons:
your parameters being passed in can be strings. later on you convert them to an array. this is expensive and goes against good coding practice. NEVER change the type of a variable after it has been initialised! Create a new variable to hold the array.
The function continues through the loop even after it has enough info to return with a result.
You basically want to start at the major version and work down to the minor version. As soon as you find one that is not equal, you want to return. It is standard when doing a greater than, less than operation in function(a,b) that the return value of the function should be :
1 = a > b
-1 = a < b
0 = a===b
Otherwise, anyone calling this function would not know what it returns true for unless they look at the internals of it. If minified and obfuscated, this could be a waste of time.
I have rewritten and improved the function with this in mind.
function (versionA, versionB) {
var arrA,
arrB;
arrA = typeof versionA !== 'object' ? versionA.toString().split('.') : versionA;
arrB = typeof versionB !== 'object' ? versionB.toString().split('.') : versionB;
for (var i = 0; i < (Math.max(arrA.length, arrB.length)); i++) {
arrA[i] = typeof arrA[i] === 'undefined' ? 0 : Number(arrA[i]);
arrB[i] = typeof arrB[i] === 'undefined' ? 0 : Number(arrB[i]);
if (arrA[i] > arrB[i]) {
return 1;
}
if (arrA[i] < arrB[i]) {
return -1;
}
}
return 0;
};

version1 = version1.toString();
version2 = version2.toString();
var matchFound = false;
if(version1.length != version2.length){
//Different Versions
}else{
//They are the same length so compare each element
FIND:
for(var i = 0; i < version1.length; i++){
var match = version[i].match(version2[i]){
if(match == ""){
//Match found
matchFound = true;
break FIND;
}
}
return matchFound;

Using localeCompare works pretty well.
The below link have my solution used in comparing version number
https://stackoverflow.com/a/73749865/13970597

Related

On the Leetcode Palindrome problem (number 234), why is my JavaScript solution not passing all the test cases?

I'm working on the palindrome question, and failing the [1,2] test case. Below is my code:
var isPalindrome = function(head) {
var listLength = head.length ? head.length : 0;
if (listLength === 0 || listLength === 1) return true;
var curNode = head[0];
var tailNode = head[listLength - 1];
if (listLength === 2) {
// if (curNode === tailNode) return true;
// return false;
return curNode === tailNode;
}
if (curNode !== tailNode) return false;
head.shift();
head.pop();
return (isPalindrome(head));
};
When I run the test case in vscode on my device, I get false for [1,2] (which is the expected result) but the uploaded version on leetcode is failing that test and returning true instead. I have no clue why. My solution is by no means the best solution but I tried a handful of tests on my local and it seemed to get the job done. Any advice on how to fix this, or insight as to why I'm failing the test case on leetcode?
Initially I thought it was the way I structured my conditional when listLength is 2, but I changed that to something I'm sure would work and it didn't change the outcome.
edit: link to leetcode question https://leetcode.com/problems/palindrome-linked-list/
As #JoshuaWood pointed out, I was operating under the assumption that my list had array properties native to JavaScript arrays, but this is an incorrect way of viewing the problem. I had to quite literally handle the list with the pointer and data they provided me. (see definition below)
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* #param {ListNode} head
* #return {boolean}
*/
Once I realized I wouldn't be able to use any array properties, and just use the properties given to me in the definition above, I was able to reformat my solution and come up with this:
var isPalindrome = function(head) {
let curr = head;
var len = 0;
let last = null;
let secondToLast = null;
if (curr.val !== null) len = 1;
if (curr && curr.next === null) return true;
while (curr.next !== null) {
len++;
secondToLast = curr;
curr = curr.next;
}
last = curr;
if (len === 2) {
if (head.val === last.val) return true;
return false;
}
if (head.val === last.val) {
secondToLast.next = null;
last.val = null;
last = null;
return isPalindrome(head.next);
} else {
return false;
}
}
Basically I can navigate to the end of the list, store what's at the end, compare it to the beginning, and pop off those values if they are equal. If they're not equal, then the list is not a Palindrome. If they are, then I iterate to the next entry in the list and pop off the end. This continues until you either reach a list with only 1 or 2 left, does the final comparison if there are two, or simply returns true if there's only one node left since that node will be equal to itself.
Hope this helps! Once I realized that of course they wouldn't have array properties, it all made a lot more sense.

How to check a Javascript array that contains at least one value that starts with a particular text (eg. ROLE_)

I have the below javascript 'underscore' code which checks if the given USER_ROLES has at least one VALID_ROLES. If so returns true else false.
It works fine.
But I want to refactor it such that I want to remove the hard coded roles VALID_ROLES and want to check if there is at least one role that starts with ROLE_. How can it be done ?
// Function to check if least one valid role is present
var USER_ROLES = ['ROLE_5'];
function hasAnyRole(USER_ROLES) {
var VALID_ROLES = [ 'ROLE_1', 'ROLE_2', 'ROLE_3', 'ROLE_4' ];
for (var i = 0; i < USER_ROLES.length; i++) {
if (_.contains(VALID_ROLES, USER_ROLES[i])) {
console.log("Found a valid role, returning true.");
return true;
}
}
console.log("No valid role found, returning false.");
return false;
}
You're pretty close, but for what you want there's no need to use underscore:
for (var i = 0; i < USER_ROLES.length; i++) {
if (typeof USER_ROLES[i].indexOf == "function" && USER_ROLES[i].indexOf("ROLE_") > -1) {
console.log("Found a valid role, returning true.");
//return true;
}
}
Use this. no need of underscore you can use .some array
USER_ROLES.some(function(value){
return value.substring(0, 5) === "ROLE_";
});
var index, value, result;
for (index = 0; index < USER_ROLES.length; ++index) {
value = USER_ROLES[index];
if (value.substring(0, 5) === "ROLE_") {
// You've found it, the full text is in `value`.
// So you might grab it and break the loop, although
// really what you do having found it depends on
// what you need.
result = value;
break;
}
}
// Use `result` here, it will be `undefined` if not found

Search within a decimal range

var a = {28.82:0, 28.91:0, 29.11:0, 30.11:0, 32.22:0, 32.23:0, 32.24:0};
function check(range) // range = 28.90;
if (a[range]){
// do stuff here...
}
In the above scenario as you can notice, the condition will never hold true. I want to write a logic where it takes into account +/- 0.01 of the range to check inside the array.
I had figured driving all array elements through a range check function but that would be too expensive.
Any thoughts?
Let's see if I understand you correctly.
You need to define a tolerance:
var a = [28.82, 28.91, 29.11, 30.11, 32.22, 32.23, 32.24];
function check(range) { // range = 28.90;
for (var i = 0; i < a.length; i++) {
if (Math.abs(a[i] - range) <= 0.01) {
// do stuff here...
}
}
}
a[i] - range will produce the difference between the value and number you're seeking. 0.01 is the set tolarance.
Maybe I'm misunderstanding, but couldn't you do this?
var a = {28.82:"a", 28.91:"b", 29.11:"c", 30.11:"d", 32.22:"e", 32.23:"f", 32.24:"g"};
function check(range){
if (a[range] || a[range+0.01] || a[range-0.01]){
// do stuff here...
}
}
Or is that the approach that you were worried would be too expensive?
Note the logical check if(a[range]) will return false if the value at a[range] equals zero or false. If you want the code to run as long as the property exists, no matter its value, you'll need to modify your condition a bit to account for those edge cases.
Edit: As #SaniHuttunen points out in the comments, you can accomplish this with if(a[range] !== undefined)
function check(range){
function propExists(key){
return a[key] !== undefined;
}
if (propExists(range) || propExists(range+0.01) || propExists(range-0.01){
// do stuff here...
}
}
Would it help to know which two elements it falls between?
var a = [28.82, 28.91, 29.11, 30.11, 32.22, 32.23, 32.24];
function check(arr, range) {
var match = [];
arr.reduce(function(prev, curr){
if ( prev <= range && range <= curr ) { match = [prev, curr]; }
return curr;
})
return match;
}
console.log(check(a, 30.90));
// [30.11, 32.22]
JSFiddle demo.

Code review - if else function

I cannot for the life of me understand why I am getting the error:
"Type error: eCurVar is undefined". Please review the code below.
var aBBTemplates = document.getElementsByClassName ("cBBTemplates");
var i = 2;
while (i < aBBTemplates.length)
{
var eCurVar = aBBTemplates[i];
if (eCurVar === e.target)
{
eCurVar.style.zIndex = 3;
// type error: eCurVar is undefined on the following line.
} else if (eCurVar.style.zIndex === 3) {
console.log (eCurVar);
eCurVar.style.zIndex = 3-1;
} else
{
console.log (eCurVar)
eCurVar.style.zIndex = i;
}
i--;
}
After each iteration i is decremented of one unit... and after three iterations it becomes negative; so you read aBBTemplates[-1] you get undefined.
When you can't understand what's going on with few console.logs, your best bet is to add a debugger; instruction, and open your devtool (usually by pressing F12).
As for your problem you could fix it by adding a check on i:
while (i < aBBTemplates.length && i >= 0) {
}
In the second case aBBTemplates[i] probably returns null
You start with i equal to 2. Lets assume that aBBTemplates[2] returns something meaningful. You do someting with it and them decrement i. Lets assume aBBTemplates[1] in meaningful.
Keep going, and sooner or lates i becomes -1, which will definately not be meaningful when reading aBBTemplates[-1]
Into if function check if also eCurrVar is not a null
if (eCurVar != null && eCurVar === e.target){
// your code
} else {
// if ecurvar == null or ecurvar != e.targer
}
Also in while check if your i is possitive number, because array do not contains items with negative indexes:
while(i >= 0 && i < aBBTemplates.length)

Simpler way to format if statement?

I have an if statement:
if(firstString == "no" && secondString == "no" && thirdString == "no"){
// Do stuff here
}
Is there a prettier way to format this? Using false instead of "no" is not an option, since the data I'm checking is from an AJAX request and I don't control its output. Otherwise I'd write it this way:
if(!firstString && !secondString && !thirdString){
// Do stuff here
}
Thanks
UPDATE:
I know this is totally ridiculous, but it occurred to me that this might actually be the shortest way:
if(firstString + secondString + thirdString == "nonono"){
// Do stuff here
}
Given that the number of strings is known in advance, then you have 2 options as far as I can see..
Leave it as it is. The if statement isn't hard to read, and any alternate formats will either be as complicated or more complicated.
convert the strings to booleans when you retrieve the data from the AJAX request, so that you're storing TRUE or FALSE instead of "yes" and "no". That would allow you to use a your preferred if statement format, and might be more efficient than many string comparisons if you do a lot of them.
In the end, which you do is up to you, but personally I think it would be better to just stick with what you've got. Don't worry about formatting an if statement, it's pretty obvious what it does, and in my opinion doesn't need to change.
If( "no" == firstString && firstString == secondString && secondString == thirdString )
It was a little difficult to determine exactly what you are evaluating to true or false, but this can be tweaked a tad to get what you're looking for.
var checkStrings = function() {
var no = "no",
args = Array.prototype.slice.call(arguments);
for (var i = 0, len = args.length; i < len; i++) {
if (args[i] !== no) {
return false;
}
}
return true;
};
if (checkStrings(firstString, secondString, thirdString)) {
// Do stuff here
}
Sorry, wasn't thinking--this is if you were checking whether ANY were 'no'
if ($.inArray('no', [firstString, secondString, thirdString]) >= 0) {
// Do something if the value is 'no'
}
UPDATED ANSWER
Unfortunately, jQuery doesn't have the reduce() function (another Array extra introduced in JS 1.6, but not available in older browsers) which would do the trick nicely.
Here's one way to check if all are 'no':
var found = true;
$.each([firstString, secondString, thirdString], function (i, str) {
if (str !== 'no') {
found = false;
}
});
It may seem uglier, but it should be shorter if you have a lot more strings to check.
If you want it wrapped in a function you could do:
function allTrue (arr, checkStr) {
var found = true;
$.each(arr, function (i, str) {
if (str !== checkStr) {
found = false;
}
});
return found;
}
if (allTrue([firstString, secondString, thirdString], 'no')) {
// ...
}
function F(var value){
return value === "no";
}
if(F(firstString) && F(secondString) && F(thirdString)){
// Do stuff here
}
Another option, using jQuery.unique:
var uniques = $.unique([firstString, secondString, thirdString]);
if (uniques.length === 1 && uniques[0] === "no") {
// do stuff
}

Categories

Resources