I am trying to compare long string numbers. The number length is between 1 and fifty. Comparing the length of the strings worked well but if they were equal length it was a bit harder. I decided to make the strings into arrays, and compare values until they were different. I think this is a formatting error, but I'm not sure.
function compareIntegers(a, b) {
//coding and coding..
var aSplit = a.split("")
var bSplit = b.split("")
if (a.length > b.length){
return "greater";
}
if (b.length > a.length){
return 'less';
}
if (a.length == b.length){
for (i=0; aSplit.length; i++){
if (bSplit.indexOf(aSplit[i] ===-1) {
if (aSplit[i] > bSplit[i]){
return 'greater';
}
if (aSplit[i] < bSplit[i]){
return 'less';
}
else return 'equal';
}
}
}
}
describe("Basic Tests", function(){
it("It should works for basic tests.", function(){
Test.assertEquals(compareIntegers("12","13"),"less")
Test.assertEquals(compareIntegers("875","799"),"greater")
Test.assertEquals(compareIntegers("1000","1000"),"equal")
Test.assertEquals(compareIntegers("999","1000"),"less")
Test.assertEquals(compareIntegers("123","122"),"greater")
Test.assertEquals(compareIntegers(
"1000000000000000000000000000000000",
"1000000000000000000000000000000001"),
"less"
)
Test.assertEquals(compareIntegers(
"1000000000000000000000000000000002",
"1000000000000000000000000000000001"),
"greater"
)
Test.assertEquals(compareIntegers(
"10000000000000000000000000000000000",
"1000000000000000000000000000000001"),
"greater"
)
})})
There's no reason to split the strings into arrays, you can easily access the single characters using an index property or .charAt(). And there's no reason to do this character-by-character comparison by yourself at all (and making mistakes like bSplit.indexOf(aSplit[i])), when that is just the default behaviour of comparing two strings. So I'd write
function compareIntegerStrings(a, b) {
a = a.replace(/^0*/, ""); // strip leading zeroes
b = b.replace(/^0*/, "");
if (a.length > b.length){
return 1;
} else if (b.length > a.length){
return -1;
} else { // a.length == b.length
if (a > b) {
return 1;
} else if (b > a) {
return -1;
}
}
return 0;
}
function comparisonToString(c) {
return ['less', 'equal', 'greater'][Math.sign(c)];
}
You can also shorten that comparison pyramid in compareIntegerStrings to
return Math.sign(a.length - b.length) || +(a>b) || -(a<b);
Related
If a number should be tested against a few ranges of values, this works :
if ((number > a && number < b)||(number > b && number < d))
{return true;}
else {return false;}
This is handy for just 2 ranges,
but it is not very convenient if there are lots of ranges of values.
Any other option here ?
Yet another way: save ranges in array and check it in loop like this
var ranges = [[a,b],[c,d]]
var inRange = ranges.some(function(el){ return number > el[0] && number < el[1]})
here use Array.some function, but it can be easy changed to simple loop.
function checkRange(ranges, value){
for(var i=0, len=ranges.length; i<len; i++){
if(value > ranges[0] && value < ranges[1]) return true;
}
return false;
}
and call it like checkRange(ranges, number)
Create a function:
function isBetween(number, low, high) {
return number > low && number < high;
}
And then test easily:
return (isBetween(number, a, b) || isBetween(number, b, d))
Note that you don't need to return value in if-statement's branches, just return the condition value.
Write a couple of simple functions and you're done.
function inRangeInclusive(num, min, max) {
return num >= min && num <= max;
}
function inRangeExclusive(num, min, max) {
return num > min && num < max;
}
let between = (n, n1, n2) => { return n >= n1 && n <= n2; }
let inRanges = (num, ranges) => {
for (let range of ranges) {
if (between(num, range[0], range[1])) return true;
}
return false;
}
console.log(inRanges(4, [[1,2], [5,10], [5, 6]]));
You can use a switch statement, for example:
switch (number) {
case (number > a && number < b):
case (number > b && number < d):
return true;
default:
return false;
}
http://jsbin.com/aboca3/95/edit
Here is working example for separate numeral and alphabetical sort.
It works well, the problem is, it doesn't sort alphabetically items with equal <em> number.
E.g. it gives Salpinestars(58), Joe Rocket (58) on numeral sort. Should give reverse order.
I've tried to change items.sort(sortEm).prependTo(self); to items.sort(sortAlpha).sort(sortEm).prependTo(self);, but it doesn't work.
Any thoughts?
Use this sortEm():
function sortEm(a,b){
var emA = parseInt($('em',a).text().replace(/[\(\)]/g,''));
var emB = parseInt($('em',b).text().replace(/[\(\)]/g,''));
if (emA == emB) { // sort alphabetically if em number are equal
return sortAlpha(a,b);
}
return emA < emB ? 1 : -1;
}
You can write one function to sort by two criterion.
// ORDER BY EmValue, LiMinusEmText
function sortBoth(a, b) {
var aText = $(a).text().replace(/\(\d+\)\s*$/, ""); // chop off the bracket
var bText = $(b).text().replace(/\(\d+\)\s*$/, ""); // and numbers portion
var aValue = +$(a).find("em").text().replace(/\D/g, ""); // parse out em value
var bValue = +$(b).find("em").text().replace(/\D/g, ""); // and convert to number
if (aValue == bValue) {
if (aText == bText) {
return 0;
}
else if (aText < bText) {
return -1;
}
else /*if (aText > bText)*/ {
return 1;
}
}
else {
return aValue - bValue;
}
}
// ORDER BY LiMinusEmText, EmValue
function sortBoth(a, b) {
var aText = $(a).text().replace(/\(\d+\)\s*$/, ""); // chop off the bracket
var bText = $(b).text().replace(/\(\d+\)\s*$/, ""); // and numbers portion
var aValue = +$(a).find("em").text().replace(/\D/g, ""); // parse out em value
var bValue = +$(b).find("em").text().replace(/\D/g, ""); // and convert to number
if (aText == bText) { // strings value same?
return aValue - bValue; // then return a - b
}
else if (aText < bText) {
return -1;
}
else /*if (aText > bText)*/ {
return 1;
}
}
So I have an array of arrays which contain only strings.
The array of arrays is to be displayed as a table and may have over 1000 rows with 20 or more values in each.
eg:
var arr = [
["bob","12","yes"],
["joe","","no"],
["tim","19","no"],
["dan","","yes"],
["tim","",""],
["dan","0",""]
]
the strings may contain anything that can be represented as a string, including: " ", "", "0" or "00-00-00" etc... and any column my be used for ordering.
I am sorting the arrays ascending and descending but some of the values I am sorting by are blank strings: "". How could I get the blank strings (only) to always be at the end of the new arrays in all modern browsers?
currently they are at the end when ascending but at the start when descending.
I am sorting like below (Yes I'm sure I can do it shorter too):
if (direction == "asc") {
SortedArr = arr.sort(function (a, b) {
if (a[colToSortBy] == '') {
return -1;
}
if (a[colToSortBy].toUpperCase() < b[colToSortBy].toUpperCase()) {
return -1;
}
if (a[colToSortBy].toUpperCase() > b[colToSortBy].toUpperCase()) {
return 1;
}
return 0;
});
} else {
SortedArr = arr.sort(function (a, b) {
if (a[colToSortBy] == '') {
return -1;
}
if (b[colToSortBy].toUpperCase() < a[colToSortBy].toUpperCase()) {
return -1;
}
if (b[colToSortBy].toUpperCase() > a[colToSortBy].toUpperCase()) {
return 1;
}
return 0;
});
}
Empty strings at the end
Working example on JSFiddle that puts empty strings always at the end no matter whether order is ascending or descending. This may be a usability issue, but this is the solution:
if (direction == "asc") {
SortedArr = arr.sort(function (a, b) {
return (a[col] || "|||").toUpperCase().localeCompare((b[col] || "|||").toUpperCase())
});
} else {
SortedArr = arr.sort(function (a, b) {
return (b[col] || "!!!").toUpperCase().localeCompare((a[col] || "!!!").toUpperCase())
});
}
I think your problem comes from the fact that you're checking if a[colToSortBy] is an emtpy string but you don't do it for b[colToSortBy].
I am writing an application that must work on IE, FF, Safari, Chrome, Opera, Desktop, Tablet (including iPad) & phones (including iPhone). This means I keep testing across browsers. I thus discovered that my sort procedure below wasn't working correctly in FF until I added the 'new section' part of the code. Reason being I did not take care of sorting when a numeric value is not supplied (dash, -). FF was also not working correctly with negative (-) values. This code now works perfectly across:
if (SortByID == 0) { //string values (Bank Name)
myValues.sort( function (a,b) {
var nameA = a[SortByID].toUpperCase(), nameB = b[SortByID].toUpperCase();
if (SortOrderID == 1) { //sort string ascending
if (nameA < nameB) { return -1; } else { if (nameA > nameB) { return 1; } }
} else { //sort string descending
if (nameA < nameB) { return 1; } else { if (nameA > nameB) { return -1; } }
}
return 0 //default return value (no sorting)
})
} else { //numeric values (Items)
myValues.sort(function (a, b) {
if (isNumber(a[SortByID]) && isNumber(b[SortByID])) { //
if (SortOrderID == 1) { //sort number ascending
return parseFloat(a[SortByID]) - parseFloat(b[SortByID]);
} else { //sort string descending
return parseFloat(b[SortByID]) - parseFloat(a[SortByID]);
}
} else { //one of the values is not numeric
//new section
if (!isNumber(a[SortByID])) {
if (SortOrderID == 1) { //sort number ascending
return -1;
} else { //sort number descending
return 1;
}
} else {
if (!isNumber(b[SortByID])) {
if (SortOrderID == 1) { //sort number ascending
return 1;
} else { //sort number descending
return -1;
}
}
}//New section
return 0;
}
})
}
I know it is long, but it is simple enough for me to understand. I hope it also addresses the browser issue raised by Chris J.
*isNumber is a simple function testing if value !isNAN
I used this way in my app...
You can tweak it to get the favorable result.
we also have the Number.MAX_SAFE_INTEGER
var arr = [10, "", 8, "", 89, 72]
var min = Number.MIN_SAFE_INTEGER
var sorted = arr.sort(function (a,b) {
return (a || min) - (b || min)
})
var cons = document.getElementById("console")
cons.innerText = "Ascending " + JSON.stringify(sorted) + "\n" + "Descending " + JSON.stringify(sorted.reverse())
<html>
<head></head>
<body>
<p id="console"></p>
</body>
</html>
I'm sorting an object array that has a primary contact name, among other things. Sometimes this has a blank value and when I use the function below it sorts it all correctly, but all the blanks go at the top of the list instead of the bottom. I thought that adding the condition shown below would work, but it does not.
this.comparePrimaryContactName = function (a, b)
{
if(a.PrimaryContactName == "") return -1;
return a.PrimaryContactName > b.PrimaryContactName ? 1 : -1;
}
What am I missing?
I usually use something like this:
this.comparePrimaryContactName = function(a, b) {
a = a.PrimaryContactName || '';
b = b.PrimaryContactName || '';
if(a.length == 0 && b.length == 0)
return 0;
else if(a.length == 0)
return 1;
else if(b.length == 0)
return -1;
else if(a > b)
return 1;
else if(a < b)
return -1;
return 0;
}
Comparison functions must be reflective, transitive, and anti-symmetric. Your function does not satisfy these criteria. For example, if two blank entries are compared with each other, you must return 0, not -1.
this.comparePrimaryContactName = function (a, b)
{
var aName = a.PrimaryContactName;
var bName = b.PrimaryContactName;
return aName === bName ? 0 :
aName.length===0 ? -1 :
bName.length===0 ? 1 :
aName > bName ? 1 : -1;
}
Return 1 instead of -1 for blanks.
this.comparePrimaryContactName = function (a, b) {
if (a.PrimaryContactName == b.PrimaryContactName)
return 0;
if(a.PrimaryContactName == "") return 1;
return a.PrimaryContactName > b.PrimaryContactName ? 1 : -1;
}
Your sort function should return 0 if the two are equal, -1 if a comes before b, and 1 if a comes after b.
See the MDN sort doco for more information.
What is the best way of sorting if the column values are:
Before SORTING:
CANCELLED,
v06.*,
INDEPENDENT,
v06.00,
v06.00.01,
v06.01,
v06.02,
v06.00.xx,
v06.03,
v06.04,
ON HOLD,
v06.06,
v06.05,
v06.05.01,
v06.04.01,
v06.05.02,
v06.07,
v07.00,
After SORTING:
CANCELLED,
INDEPENDENT,
ON HOLD,
v06.*,
v06.00,
v06.00.01,
v06.00.xx,
v06.01,
v06.02,
v06.03,
v06.04,
v06.04.01,
v06.05,
v06.05.01,
v06.05.02,
v06.06,
v06.07,
v07.00
Thanks in advance,
Joseph
First sort it alphabetically ascending (by the 'default' sort - or sort() without passing a function), then sort it numerically. That said, I'm sure there is a better way:
function sortNumber(a, b) {
return a - b;
}
var arr = [
"v07.00", "CANCELLED", "v06.*", "v06.04.01", "INDEPENDENT", "v06.00", "v06.00.01",
"v06.01", "v06.02", "v06.00.xx", "v06.03", "v06.04", "ON HOLD",
"v06.06", "v06.05", "v06.05.01", "v06.05.02",
"v06.07",
];
alert(arr.sort().sort(sortNumber).join("\n"));
Demo: http://jsfiddle.net/karim79/rY8Du/1/
Assuming your "column values" are in an Array, use Array.sort with a custom compareFunction to define the ordering as you want.
var columnValues = [
"CANCELLED", "v06.*", "INDEPENDENT", "v06.00", "v06.00.01",
"v06.01", "v06.02", "v06.00.xx", "v06.03", "v06.04", "ON HOLD",
"v06.06", "v06.05", "v06.05.01", "v06.04.01", "v06.05.02",
"v06.07", "v07.00" ];
columnValues.sort(function(a, b) {
if (a is less than b by some ordering criterion)
return -1;
if (a is greater than b by the ordering criterion)
return 1;
// a must be equal to b
return 0;
});
Edit here's a long-winded compareFunction that seems to do what you want, at least for the example you give:
function(a, b) {
if (a==b) {
return 0;
}
if (a.length && a[0]=='v' && b.length && b[0]=='v') {
// Both strings are version strings.
// Do special case version matching.
var aParts = a.substring(1).split('.'),
bParts = b.substring(1).split('.'),
l = Math.max(a.length, b.length),
i = 0;
for (;i<l;i++) {
var aPart = aParts[i],
bPart = bParts[i];
if (aPart == '*' && bPart != '*') {
return -1;
}
if (bPart == '*' && aPart != '*') {
return 1;
}
if (aPart == 'xx' && bPart != 'xx') {
return 1;
}
if (bPart == 'xx' && aPart != 'xx') {
return -1;
}
var aNum = parseInt(aPart,10),
bNum = parseInt(bPart,10);
if (aNum < bNum) {
return -1;
}
if (aNum > bNum) {
return 1;
}
// Same so far, try next part
}
// One must be longer than the other.
return (aParts.length < bParts.length) ? -1 : 1
}
// Simple alphabetic comparison
if (a < b)
return -1;
if (a > b)
return 1;
}
Demo: http://jsfiddle.net/daybarr/h6nmg/
Once you get the column into an array you can use any natural sort method,
but you'll need to write a couple extra lines to sort the '*' the way you want.
Here is one sample-
function natSort(as, bs){
var a, b, a1, b1, i= 0, L, rx= /(\d+)|(\D+)/g, rd= /\d/;
if(isFinite(as) && isFinite(bs)) return as - bs;
a= String(as).toLowerCase();
b= String(bs).toLowerCase();
if(a=== b) return 0;
if(!(rd.test(a) && rd.test(b))) return a> b? 1: -1;
a=a.replace('*','0'); // or possibly sort any non alpha nums first:
b=b.replace('*','0'); // replace(/[^\w\.]/g,'0');
a= a.match(rx);
b= b.match(rx);
L= a.length> b.length? b.length: a.length;
while(i < L){
a1= a[i];
b1= b[i++];
if(a1!== b1){
if(isFinite(a1) && isFinite(b1)){
if(a1.charAt(0)=== "0") a1= "." + a1;
if(b1.charAt(0)=== "0") b1= "." + b1;
return a1 - b1;
}
else return a1> b1? 1: -1;
}
}
return a.length - b.length;
}
var v= "v06.05,ON HOLD,v07.00,INDEPENDENT,v06.07,v06.03,v06.*,v06.05.02,v06.05.01,"+
"v06.00.xx,v06.00,CANCELLED,v06.02,v06.04,v06.00.01,v06.06,v06.01,v06.04.01";
v=v.split(/, */);
'before sort:\n'+v.join(',\n')+'\n\nafter sort:\n'+ v.sort(natSort).join(',\n')
/* returned value:
before sort:
v06.05,
ON HOLD,
v07.00,
INDEPENDENT,
v06.07,
v06.03,
v06.*,
v06.05.02,
v06.05.01,
v06.00.xx,
v06.00,
CANCELLED,
v06.02,
v06.04,
v06.00.01,
v06.06,
v06.01,
v06.04.01
after sort:
CANCELLED,
INDEPENDENT,
ON HOLD,
v06.*,
v06.00,
v06.00.01,
v06.00.xx,
v06.01,
v06.02,
v06.03,
v06.04,
v06.04.01,
v06.05,
v06.05.01,
v06.05.02,
v06.06,
v06.07,
v07.00
*/