Comparing 2 iOS and Android versions Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)Comparing two bubble sort algorithmsPython script to create Android/iOS strings from a .csvOOP and non-OOP versions of layout and page animation codeParse 2D matrix, 2 versionsTwo versions of SudokuSmall camera app in react-native for AndroidCustom header with React-Navigation and React-Native-PaperMultiple stacks implemented via a linked lists on top of single fixed-size arrayTwo versions of PDF-SelectorThree versions of a counting-up timer in JavaScript
What is the meaning of the simile “quick as silk”?
What is the longest distance a player character can jump in one leap?
8 Prisoners wearing hats
Should I use a zero-interest credit card for a large one-time purchase?
Maximum summed powersets with non-adjacent items
Do I really need to have a message in a novel to appeal to readers?
Can anything be seen from the center of the Boötes void? How dark would it be?
Delete nth line from bottom
How to react to hostile behavior from a senior developer?
Is it ethical to give a final exam after the professor has quit before teaching the remaining chapters of the course?
Is safe to use va_start macro with this as parameter?
Does classifying an integer as a discrete log require it be part of a multiplicative group?
What are the out-of-universe reasons for the references to Toby Maguire-era Spider-Man in ITSV
First console to have temporary backward compatibility
What causes the direction of lightning flashes?
What is homebrew?
Dating a Former Employee
What do you call a floor made of glass so you can see through the floor?
Can a new player join a group only when a new campaign starts?
What does the "x" in "x86" represent?
What do you call the main part of a joke?
How could we fake a moon landing now?
Fantasy story; one type of magic grows in power with use, but the more powerful they are, they more they are drawn to travel to their source
How do I make this wiring inside cabinet safer? (Pic)
Comparing 2 iOS and Android versions
Announcing the arrival of Valued Associate #679: Cesar Manara
Planned maintenance scheduled April 17/18, 2019 at 00:00UTC (8:00pm US/Eastern)Comparing two bubble sort algorithmsPython script to create Android/iOS strings from a .csvOOP and non-OOP versions of layout and page animation codeParse 2D matrix, 2 versionsTwo versions of SudokuSmall camera app in react-native for AndroidCustom header with React-Navigation and React-Native-PaperMultiple stacks implemented via a linked lists on top of single fixed-size arrayTwo versions of PDF-SelectorThree versions of a counting-up timer in JavaScript
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
Please review and let me know which version is better. I'm just trying to find any caveats or better ways to accomplish the a version comparison independent from Android and iOS.
Formats accepted: '1.0.0' or '1' formats and an operator, e.g: compareVersion('1', '1.2.0', '>')
export function compareVersion(version1, version2, operator)
const formattedV1 = version1.split(".");
const formattedV2 = version2.split(".");
let diff = 0;
if (formattedV1.length !== formattedV2.length)
const lengthDiff = formattedV1.length - formattedV2.length;
for (let index = 0; index < Math.abs(lengthDiff); index += 1)
if (lengthDiff > 0)
formattedV2.push("0");
else
formattedV1.push("0");
for (let index = 0; index < formattedV1.length; index += 1)
if (diff === 0)
const v1 = parseInt(formattedV1[index]);
const v2 = parseInt(formattedV2[index]);
if (isNaN(v1)
switch (operator)
case "=":
case "==":
return diff === 0;
case ">=":
return diff >= 0;
case "<=":
return diff <= 0;
case ">":
return diff > 0;
case "<":
return diff < 0;
default:
throw new Error("Problem comparing versions");
OR
export function compareVersions(a, b, operator)
const aParts = a.split('.');
const bParts = b.split('.');
const pairs = [];
for (let index = 0; index < Math.max(aParts.length, bParts.length); index += 1)
pairs.push(
a: parseInt(aParts[index]),
b: parseInt(bParts[index]),
);
let diff = 0;
pairs.forEach((pair) =>
if (diff === 0)
if (pair.a > pair.b)
diff = 1;
if (pair.b > pair.a)
diff = -1;
if (!isNaN(pair.a) && isNaN(pair.b))
diff = 1;
if (isNaN(pair.a) && !isNaN(pair.b))
diff = -1;
);
switch (operator)
case '=':
case '==':
return diff === 0;
case '>=':
return diff >= 0;
case '<=':
return diff <= 0;
case '>':
return diff > 0;
case '<':
return diff < 0;
default:
throw new Error('Problem comparing versions');
javascript beginner comparative-review react-native
New contributor
$endgroup$
add a comment |
$begingroup$
Please review and let me know which version is better. I'm just trying to find any caveats or better ways to accomplish the a version comparison independent from Android and iOS.
Formats accepted: '1.0.0' or '1' formats and an operator, e.g: compareVersion('1', '1.2.0', '>')
export function compareVersion(version1, version2, operator)
const formattedV1 = version1.split(".");
const formattedV2 = version2.split(".");
let diff = 0;
if (formattedV1.length !== formattedV2.length)
const lengthDiff = formattedV1.length - formattedV2.length;
for (let index = 0; index < Math.abs(lengthDiff); index += 1)
if (lengthDiff > 0)
formattedV2.push("0");
else
formattedV1.push("0");
for (let index = 0; index < formattedV1.length; index += 1)
if (diff === 0)
const v1 = parseInt(formattedV1[index]);
const v2 = parseInt(formattedV2[index]);
if (isNaN(v1)
switch (operator)
case "=":
case "==":
return diff === 0;
case ">=":
return diff >= 0;
case "<=":
return diff <= 0;
case ">":
return diff > 0;
case "<":
return diff < 0;
default:
throw new Error("Problem comparing versions");
OR
export function compareVersions(a, b, operator)
const aParts = a.split('.');
const bParts = b.split('.');
const pairs = [];
for (let index = 0; index < Math.max(aParts.length, bParts.length); index += 1)
pairs.push(
a: parseInt(aParts[index]),
b: parseInt(bParts[index]),
);
let diff = 0;
pairs.forEach((pair) =>
if (diff === 0)
if (pair.a > pair.b)
diff = 1;
if (pair.b > pair.a)
diff = -1;
if (!isNaN(pair.a) && isNaN(pair.b))
diff = 1;
if (isNaN(pair.a) && !isNaN(pair.b))
diff = -1;
);
switch (operator)
case '=':
case '==':
return diff === 0;
case '>=':
return diff >= 0;
case '<=':
return diff <= 0;
case '>':
return diff > 0;
case '<':
return diff < 0;
default:
throw new Error('Problem comparing versions');
javascript beginner comparative-review react-native
New contributor
$endgroup$
add a comment |
$begingroup$
Please review and let me know which version is better. I'm just trying to find any caveats or better ways to accomplish the a version comparison independent from Android and iOS.
Formats accepted: '1.0.0' or '1' formats and an operator, e.g: compareVersion('1', '1.2.0', '>')
export function compareVersion(version1, version2, operator)
const formattedV1 = version1.split(".");
const formattedV2 = version2.split(".");
let diff = 0;
if (formattedV1.length !== formattedV2.length)
const lengthDiff = formattedV1.length - formattedV2.length;
for (let index = 0; index < Math.abs(lengthDiff); index += 1)
if (lengthDiff > 0)
formattedV2.push("0");
else
formattedV1.push("0");
for (let index = 0; index < formattedV1.length; index += 1)
if (diff === 0)
const v1 = parseInt(formattedV1[index]);
const v2 = parseInt(formattedV2[index]);
if (isNaN(v1)
switch (operator)
case "=":
case "==":
return diff === 0;
case ">=":
return diff >= 0;
case "<=":
return diff <= 0;
case ">":
return diff > 0;
case "<":
return diff < 0;
default:
throw new Error("Problem comparing versions");
OR
export function compareVersions(a, b, operator)
const aParts = a.split('.');
const bParts = b.split('.');
const pairs = [];
for (let index = 0; index < Math.max(aParts.length, bParts.length); index += 1)
pairs.push(
a: parseInt(aParts[index]),
b: parseInt(bParts[index]),
);
let diff = 0;
pairs.forEach((pair) =>
if (diff === 0)
if (pair.a > pair.b)
diff = 1;
if (pair.b > pair.a)
diff = -1;
if (!isNaN(pair.a) && isNaN(pair.b))
diff = 1;
if (isNaN(pair.a) && !isNaN(pair.b))
diff = -1;
);
switch (operator)
case '=':
case '==':
return diff === 0;
case '>=':
return diff >= 0;
case '<=':
return diff <= 0;
case '>':
return diff > 0;
case '<':
return diff < 0;
default:
throw new Error('Problem comparing versions');
javascript beginner comparative-review react-native
New contributor
$endgroup$
Please review and let me know which version is better. I'm just trying to find any caveats or better ways to accomplish the a version comparison independent from Android and iOS.
Formats accepted: '1.0.0' or '1' formats and an operator, e.g: compareVersion('1', '1.2.0', '>')
export function compareVersion(version1, version2, operator)
const formattedV1 = version1.split(".");
const formattedV2 = version2.split(".");
let diff = 0;
if (formattedV1.length !== formattedV2.length)
const lengthDiff = formattedV1.length - formattedV2.length;
for (let index = 0; index < Math.abs(lengthDiff); index += 1)
if (lengthDiff > 0)
formattedV2.push("0");
else
formattedV1.push("0");
for (let index = 0; index < formattedV1.length; index += 1)
if (diff === 0)
const v1 = parseInt(formattedV1[index]);
const v2 = parseInt(formattedV2[index]);
if (isNaN(v1)
switch (operator)
case "=":
case "==":
return diff === 0;
case ">=":
return diff >= 0;
case "<=":
return diff <= 0;
case ">":
return diff > 0;
case "<":
return diff < 0;
default:
throw new Error("Problem comparing versions");
OR
export function compareVersions(a, b, operator)
const aParts = a.split('.');
const bParts = b.split('.');
const pairs = [];
for (let index = 0; index < Math.max(aParts.length, bParts.length); index += 1)
pairs.push(
a: parseInt(aParts[index]),
b: parseInt(bParts[index]),
);
let diff = 0;
pairs.forEach((pair) =>
if (diff === 0)
if (pair.a > pair.b)
diff = 1;
if (pair.b > pair.a)
diff = -1;
if (!isNaN(pair.a) && isNaN(pair.b))
diff = 1;
if (isNaN(pair.a) && !isNaN(pair.b))
diff = -1;
);
switch (operator)
case '=':
case '==':
return diff === 0;
case '>=':
return diff >= 0;
case '<=':
return diff <= 0;
case '>':
return diff > 0;
case '<':
return diff < 0;
default:
throw new Error('Problem comparing versions');
javascript beginner comparative-review react-native
javascript beginner comparative-review react-native
New contributor
New contributor
edited 6 mins ago
Jamal♦
30.6k11121227
30.6k11121227
New contributor
asked yesterday
VincentVincent
211
211
New contributor
New contributor
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
One role
Be careful to do only what the functions should.
The function is called compareVersions
however what it does is validate and compare versions.
The potential throw is the result of validation and has nothing to do with comparing the values.
As you have not called the function validateAndCompareVersions
you have over stepped its role.
How to throw
If (and try to avoid throwing exceptions), if you must throw, throw correctly.
In this case the first throw should be a RangeError
and the message should make sense.
You have very poor error message "Problem comparing versions: not a valid number";
The first part is redundant, exception contains a trace that will locate the "problem" and the "Problem" is implied in the fact that this is an exception.
"not a valid number" The arguments are version strings, "1.0.0" is not a number. The error should indicate the problem explicitly.
throw new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
The second exception is a little better, but could be improved
throw new RangeError("Invalid operator: " + o);
How to catch
Only catch what is thrown for you. Higher level catchers will handle the rest.
Having the throws in the function means you must add additional support code outside the function. Try catches break the flow of the code, not having a try catch means that a input error will halt the app.
try
compareVersions(a,b,type);
catch(e)
if (e.name === "RangeError")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
You may need to be a little more specific, you can either extend an existing error or define a new error. Extending is the JS way so using the name property
// in function
const error = new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
error.name = "InvalidVersionStr";
throw error;
// outside function
try
compareVersions(a,b,type);
catch(e)
if (e.name === "InvalidVersionStr")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
Validate or normalize
It is best to avoid exceptions.
In JavaScript we can pretend we have a 3 state Boolean, true
, false
, and undefined
. We can use the 3rd state to signal an error without needing to break flow or halt execution. You can return undefined with return;
Then you calling function need only handle the undefined
const versionResult = compareVersions(a, b, type);
if (versionResult === undefined) /* put the spanner here */
Better yet the function should assume all is good and just return true or false.
Validate the version strings at the source and deal with it when you get it (that is where the problem actually is)
You can either validate the version string or normalize the string
function normaliseVersionStr(ver) ^d*$/.test(ver) ? ver : "0";
function validateVersionStr(ver) ^d*$/.test(ver);
// Good strings returned as they are
normaliseVersionStr("1.0.0")
normaliseVersionStr("1")
normaliseVersionStr("1.000.0")
// Bad string returned as version 0
normaliseVersionStr("1.0.0A")
normaliseVersionStr("1.")
normaliseVersionStr("1.0")
normaliseVersionStr("")
Alternative solution
Now that you can trust the arguments you get you can write a better function as you dont need to bother with all the possible edge cases.
There are many ways to do this, and they depend on what you define as same, greater, less. I will assume the following
01 == 1 == 1.0.0 == 01.0.0 == 1.00.00
0.01.0 == 0.1.0
0.0.1 < 0.1.0
2.0.1 < 2.1.0
1.9.0 < 2
If we then pad the strings to match (each version str) parts sizes, and remove leading zeros.
1 and 1.0.0 become 100 and 100
1 and 2 become 1 and 2
1 and 0.0.1 become 100 and 1
1.99.0 and 1 become 1990 and 1000
Then use eval
to do the final operation. If you don't like eval
, you can use new Function
Example
It is assumed that the operator is defined in the source (not as an user input). If the operator is a user input string then you should validate or normalize that string before calling the function.
An invalid operator will throw an exception.
// logical operator optional. Default "=="
// "==", "<", ">", "<=", ">="
// "=" is considered to be "=="
// can also use "!=", "!==", "==="
// strA, strB must be valid version strings
function compareVersionStrings(strA, strB, operator = "==")
const a = strA.split("."), b = strB.split(".");
const len = Math.max(a.length, b.length);
var valA = a.shift(), valB = b.shift(), i = 1;
while (i < len)
const vA = a[i] !== undefined ? a[i] : "";
const vB = b[i] !== undefined ? b[i] : "";
const digits = Math.max(vA.length, vB.length);
valA += vA.padStart(digits, "0");
valB += vB.padStart(digits, "0");
i++;
valA = valA.replace(/0*d$/,"");
valB = valB.replace(/0*d$/,"");
operator = operator === "=" ? "==" : operator;
return eval(`$valA $operator $valB`);
// or
return (new Function(`return $valA $operator $valB)`)();
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Vincent is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217593%2fcomparing-2-ios-and-android-versions%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
One role
Be careful to do only what the functions should.
The function is called compareVersions
however what it does is validate and compare versions.
The potential throw is the result of validation and has nothing to do with comparing the values.
As you have not called the function validateAndCompareVersions
you have over stepped its role.
How to throw
If (and try to avoid throwing exceptions), if you must throw, throw correctly.
In this case the first throw should be a RangeError
and the message should make sense.
You have very poor error message "Problem comparing versions: not a valid number";
The first part is redundant, exception contains a trace that will locate the "problem" and the "Problem" is implied in the fact that this is an exception.
"not a valid number" The arguments are version strings, "1.0.0" is not a number. The error should indicate the problem explicitly.
throw new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
The second exception is a little better, but could be improved
throw new RangeError("Invalid operator: " + o);
How to catch
Only catch what is thrown for you. Higher level catchers will handle the rest.
Having the throws in the function means you must add additional support code outside the function. Try catches break the flow of the code, not having a try catch means that a input error will halt the app.
try
compareVersions(a,b,type);
catch(e)
if (e.name === "RangeError")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
You may need to be a little more specific, you can either extend an existing error or define a new error. Extending is the JS way so using the name property
// in function
const error = new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
error.name = "InvalidVersionStr";
throw error;
// outside function
try
compareVersions(a,b,type);
catch(e)
if (e.name === "InvalidVersionStr")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
Validate or normalize
It is best to avoid exceptions.
In JavaScript we can pretend we have a 3 state Boolean, true
, false
, and undefined
. We can use the 3rd state to signal an error without needing to break flow or halt execution. You can return undefined with return;
Then you calling function need only handle the undefined
const versionResult = compareVersions(a, b, type);
if (versionResult === undefined) /* put the spanner here */
Better yet the function should assume all is good and just return true or false.
Validate the version strings at the source and deal with it when you get it (that is where the problem actually is)
You can either validate the version string or normalize the string
function normaliseVersionStr(ver) ^d*$/.test(ver) ? ver : "0";
function validateVersionStr(ver) ^d*$/.test(ver);
// Good strings returned as they are
normaliseVersionStr("1.0.0")
normaliseVersionStr("1")
normaliseVersionStr("1.000.0")
// Bad string returned as version 0
normaliseVersionStr("1.0.0A")
normaliseVersionStr("1.")
normaliseVersionStr("1.0")
normaliseVersionStr("")
Alternative solution
Now that you can trust the arguments you get you can write a better function as you dont need to bother with all the possible edge cases.
There are many ways to do this, and they depend on what you define as same, greater, less. I will assume the following
01 == 1 == 1.0.0 == 01.0.0 == 1.00.00
0.01.0 == 0.1.0
0.0.1 < 0.1.0
2.0.1 < 2.1.0
1.9.0 < 2
If we then pad the strings to match (each version str) parts sizes, and remove leading zeros.
1 and 1.0.0 become 100 and 100
1 and 2 become 1 and 2
1 and 0.0.1 become 100 and 1
1.99.0 and 1 become 1990 and 1000
Then use eval
to do the final operation. If you don't like eval
, you can use new Function
Example
It is assumed that the operator is defined in the source (not as an user input). If the operator is a user input string then you should validate or normalize that string before calling the function.
An invalid operator will throw an exception.
// logical operator optional. Default "=="
// "==", "<", ">", "<=", ">="
// "=" is considered to be "=="
// can also use "!=", "!==", "==="
// strA, strB must be valid version strings
function compareVersionStrings(strA, strB, operator = "==")
const a = strA.split("."), b = strB.split(".");
const len = Math.max(a.length, b.length);
var valA = a.shift(), valB = b.shift(), i = 1;
while (i < len)
const vA = a[i] !== undefined ? a[i] : "";
const vB = b[i] !== undefined ? b[i] : "";
const digits = Math.max(vA.length, vB.length);
valA += vA.padStart(digits, "0");
valB += vB.padStart(digits, "0");
i++;
valA = valA.replace(/0*d$/,"");
valB = valB.replace(/0*d$/,"");
operator = operator === "=" ? "==" : operator;
return eval(`$valA $operator $valB`);
// or
return (new Function(`return $valA $operator $valB)`)();
$endgroup$
add a comment |
$begingroup$
One role
Be careful to do only what the functions should.
The function is called compareVersions
however what it does is validate and compare versions.
The potential throw is the result of validation and has nothing to do with comparing the values.
As you have not called the function validateAndCompareVersions
you have over stepped its role.
How to throw
If (and try to avoid throwing exceptions), if you must throw, throw correctly.
In this case the first throw should be a RangeError
and the message should make sense.
You have very poor error message "Problem comparing versions: not a valid number";
The first part is redundant, exception contains a trace that will locate the "problem" and the "Problem" is implied in the fact that this is an exception.
"not a valid number" The arguments are version strings, "1.0.0" is not a number. The error should indicate the problem explicitly.
throw new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
The second exception is a little better, but could be improved
throw new RangeError("Invalid operator: " + o);
How to catch
Only catch what is thrown for you. Higher level catchers will handle the rest.
Having the throws in the function means you must add additional support code outside the function. Try catches break the flow of the code, not having a try catch means that a input error will halt the app.
try
compareVersions(a,b,type);
catch(e)
if (e.name === "RangeError")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
You may need to be a little more specific, you can either extend an existing error or define a new error. Extending is the JS way so using the name property
// in function
const error = new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
error.name = "InvalidVersionStr";
throw error;
// outside function
try
compareVersions(a,b,type);
catch(e)
if (e.name === "InvalidVersionStr")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
Validate or normalize
It is best to avoid exceptions.
In JavaScript we can pretend we have a 3 state Boolean, true
, false
, and undefined
. We can use the 3rd state to signal an error without needing to break flow or halt execution. You can return undefined with return;
Then you calling function need only handle the undefined
const versionResult = compareVersions(a, b, type);
if (versionResult === undefined) /* put the spanner here */
Better yet the function should assume all is good and just return true or false.
Validate the version strings at the source and deal with it when you get it (that is where the problem actually is)
You can either validate the version string or normalize the string
function normaliseVersionStr(ver) ^d*$/.test(ver) ? ver : "0";
function validateVersionStr(ver) ^d*$/.test(ver);
// Good strings returned as they are
normaliseVersionStr("1.0.0")
normaliseVersionStr("1")
normaliseVersionStr("1.000.0")
// Bad string returned as version 0
normaliseVersionStr("1.0.0A")
normaliseVersionStr("1.")
normaliseVersionStr("1.0")
normaliseVersionStr("")
Alternative solution
Now that you can trust the arguments you get you can write a better function as you dont need to bother with all the possible edge cases.
There are many ways to do this, and they depend on what you define as same, greater, less. I will assume the following
01 == 1 == 1.0.0 == 01.0.0 == 1.00.00
0.01.0 == 0.1.0
0.0.1 < 0.1.0
2.0.1 < 2.1.0
1.9.0 < 2
If we then pad the strings to match (each version str) parts sizes, and remove leading zeros.
1 and 1.0.0 become 100 and 100
1 and 2 become 1 and 2
1 and 0.0.1 become 100 and 1
1.99.0 and 1 become 1990 and 1000
Then use eval
to do the final operation. If you don't like eval
, you can use new Function
Example
It is assumed that the operator is defined in the source (not as an user input). If the operator is a user input string then you should validate or normalize that string before calling the function.
An invalid operator will throw an exception.
// logical operator optional. Default "=="
// "==", "<", ">", "<=", ">="
// "=" is considered to be "=="
// can also use "!=", "!==", "==="
// strA, strB must be valid version strings
function compareVersionStrings(strA, strB, operator = "==")
const a = strA.split("."), b = strB.split(".");
const len = Math.max(a.length, b.length);
var valA = a.shift(), valB = b.shift(), i = 1;
while (i < len)
const vA = a[i] !== undefined ? a[i] : "";
const vB = b[i] !== undefined ? b[i] : "";
const digits = Math.max(vA.length, vB.length);
valA += vA.padStart(digits, "0");
valB += vB.padStart(digits, "0");
i++;
valA = valA.replace(/0*d$/,"");
valB = valB.replace(/0*d$/,"");
operator = operator === "=" ? "==" : operator;
return eval(`$valA $operator $valB`);
// or
return (new Function(`return $valA $operator $valB)`)();
$endgroup$
add a comment |
$begingroup$
One role
Be careful to do only what the functions should.
The function is called compareVersions
however what it does is validate and compare versions.
The potential throw is the result of validation and has nothing to do with comparing the values.
As you have not called the function validateAndCompareVersions
you have over stepped its role.
How to throw
If (and try to avoid throwing exceptions), if you must throw, throw correctly.
In this case the first throw should be a RangeError
and the message should make sense.
You have very poor error message "Problem comparing versions: not a valid number";
The first part is redundant, exception contains a trace that will locate the "problem" and the "Problem" is implied in the fact that this is an exception.
"not a valid number" The arguments are version strings, "1.0.0" is not a number. The error should indicate the problem explicitly.
throw new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
The second exception is a little better, but could be improved
throw new RangeError("Invalid operator: " + o);
How to catch
Only catch what is thrown for you. Higher level catchers will handle the rest.
Having the throws in the function means you must add additional support code outside the function. Try catches break the flow of the code, not having a try catch means that a input error will halt the app.
try
compareVersions(a,b,type);
catch(e)
if (e.name === "RangeError")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
You may need to be a little more specific, you can either extend an existing error or define a new error. Extending is the JS way so using the name property
// in function
const error = new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
error.name = "InvalidVersionStr";
throw error;
// outside function
try
compareVersions(a,b,type);
catch(e)
if (e.name === "InvalidVersionStr")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
Validate or normalize
It is best to avoid exceptions.
In JavaScript we can pretend we have a 3 state Boolean, true
, false
, and undefined
. We can use the 3rd state to signal an error without needing to break flow or halt execution. You can return undefined with return;
Then you calling function need only handle the undefined
const versionResult = compareVersions(a, b, type);
if (versionResult === undefined) /* put the spanner here */
Better yet the function should assume all is good and just return true or false.
Validate the version strings at the source and deal with it when you get it (that is where the problem actually is)
You can either validate the version string or normalize the string
function normaliseVersionStr(ver) ^d*$/.test(ver) ? ver : "0";
function validateVersionStr(ver) ^d*$/.test(ver);
// Good strings returned as they are
normaliseVersionStr("1.0.0")
normaliseVersionStr("1")
normaliseVersionStr("1.000.0")
// Bad string returned as version 0
normaliseVersionStr("1.0.0A")
normaliseVersionStr("1.")
normaliseVersionStr("1.0")
normaliseVersionStr("")
Alternative solution
Now that you can trust the arguments you get you can write a better function as you dont need to bother with all the possible edge cases.
There are many ways to do this, and they depend on what you define as same, greater, less. I will assume the following
01 == 1 == 1.0.0 == 01.0.0 == 1.00.00
0.01.0 == 0.1.0
0.0.1 < 0.1.0
2.0.1 < 2.1.0
1.9.0 < 2
If we then pad the strings to match (each version str) parts sizes, and remove leading zeros.
1 and 1.0.0 become 100 and 100
1 and 2 become 1 and 2
1 and 0.0.1 become 100 and 1
1.99.0 and 1 become 1990 and 1000
Then use eval
to do the final operation. If you don't like eval
, you can use new Function
Example
It is assumed that the operator is defined in the source (not as an user input). If the operator is a user input string then you should validate or normalize that string before calling the function.
An invalid operator will throw an exception.
// logical operator optional. Default "=="
// "==", "<", ">", "<=", ">="
// "=" is considered to be "=="
// can also use "!=", "!==", "==="
// strA, strB must be valid version strings
function compareVersionStrings(strA, strB, operator = "==")
const a = strA.split("."), b = strB.split(".");
const len = Math.max(a.length, b.length);
var valA = a.shift(), valB = b.shift(), i = 1;
while (i < len)
const vA = a[i] !== undefined ? a[i] : "";
const vB = b[i] !== undefined ? b[i] : "";
const digits = Math.max(vA.length, vB.length);
valA += vA.padStart(digits, "0");
valB += vB.padStart(digits, "0");
i++;
valA = valA.replace(/0*d$/,"");
valB = valB.replace(/0*d$/,"");
operator = operator === "=" ? "==" : operator;
return eval(`$valA $operator $valB`);
// or
return (new Function(`return $valA $operator $valB)`)();
$endgroup$
One role
Be careful to do only what the functions should.
The function is called compareVersions
however what it does is validate and compare versions.
The potential throw is the result of validation and has nothing to do with comparing the values.
As you have not called the function validateAndCompareVersions
you have over stepped its role.
How to throw
If (and try to avoid throwing exceptions), if you must throw, throw correctly.
In this case the first throw should be a RangeError
and the message should make sense.
You have very poor error message "Problem comparing versions: not a valid number";
The first part is redundant, exception contains a trace that will locate the "problem" and the "Problem" is implied in the fact that this is an exception.
"not a valid number" The arguments are version strings, "1.0.0" is not a number. The error should indicate the problem explicitly.
throw new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
The second exception is a little better, but could be improved
throw new RangeError("Invalid operator: " + o);
How to catch
Only catch what is thrown for you. Higher level catchers will handle the rest.
Having the throws in the function means you must add additional support code outside the function. Try catches break the flow of the code, not having a try catch means that a input error will halt the app.
try
compareVersions(a,b,type);
catch(e)
if (e.name === "RangeError")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
You may need to be a little more specific, you can either extend an existing error or define a new error. Extending is the JS way so using the name property
// in function
const error = new RangeError("Invalid version string: '" + (isNaN(v1) ? v1 : v2) + "'");
error.name = "InvalidVersionStr";
throw error;
// outside function
try
compareVersions(a,b,type);
catch(e)
if (e.name === "InvalidVersionStr")
// do what is needed to prevent app from stopping
else
throw e; // rethrow for development cycle or high level catchers
Validate or normalize
It is best to avoid exceptions.
In JavaScript we can pretend we have a 3 state Boolean, true
, false
, and undefined
. We can use the 3rd state to signal an error without needing to break flow or halt execution. You can return undefined with return;
Then you calling function need only handle the undefined
const versionResult = compareVersions(a, b, type);
if (versionResult === undefined) /* put the spanner here */
Better yet the function should assume all is good and just return true or false.
Validate the version strings at the source and deal with it when you get it (that is where the problem actually is)
You can either validate the version string or normalize the string
function normaliseVersionStr(ver) ^d*$/.test(ver) ? ver : "0";
function validateVersionStr(ver) ^d*$/.test(ver);
// Good strings returned as they are
normaliseVersionStr("1.0.0")
normaliseVersionStr("1")
normaliseVersionStr("1.000.0")
// Bad string returned as version 0
normaliseVersionStr("1.0.0A")
normaliseVersionStr("1.")
normaliseVersionStr("1.0")
normaliseVersionStr("")
Alternative solution
Now that you can trust the arguments you get you can write a better function as you dont need to bother with all the possible edge cases.
There are many ways to do this, and they depend on what you define as same, greater, less. I will assume the following
01 == 1 == 1.0.0 == 01.0.0 == 1.00.00
0.01.0 == 0.1.0
0.0.1 < 0.1.0
2.0.1 < 2.1.0
1.9.0 < 2
If we then pad the strings to match (each version str) parts sizes, and remove leading zeros.
1 and 1.0.0 become 100 and 100
1 and 2 become 1 and 2
1 and 0.0.1 become 100 and 1
1.99.0 and 1 become 1990 and 1000
Then use eval
to do the final operation. If you don't like eval
, you can use new Function
Example
It is assumed that the operator is defined in the source (not as an user input). If the operator is a user input string then you should validate or normalize that string before calling the function.
An invalid operator will throw an exception.
// logical operator optional. Default "=="
// "==", "<", ">", "<=", ">="
// "=" is considered to be "=="
// can also use "!=", "!==", "==="
// strA, strB must be valid version strings
function compareVersionStrings(strA, strB, operator = "==")
const a = strA.split("."), b = strB.split(".");
const len = Math.max(a.length, b.length);
var valA = a.shift(), valB = b.shift(), i = 1;
while (i < len)
const vA = a[i] !== undefined ? a[i] : "";
const vB = b[i] !== undefined ? b[i] : "";
const digits = Math.max(vA.length, vB.length);
valA += vA.padStart(digits, "0");
valB += vB.padStart(digits, "0");
i++;
valA = valA.replace(/0*d$/,"");
valB = valB.replace(/0*d$/,"");
operator = operator === "=" ? "==" : operator;
return eval(`$valA $operator $valB`);
// or
return (new Function(`return $valA $operator $valB)`)();
answered 14 hours ago
Blindman67Blindman67
9,6801622
9,6801622
add a comment |
add a comment |
Vincent is a new contributor. Be nice, and check out our Code of Conduct.
Vincent is a new contributor. Be nice, and check out our Code of Conduct.
Vincent is a new contributor. Be nice, and check out our Code of Conduct.
Vincent is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217593%2fcomparing-2-ios-and-android-versions%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown