How to sort strings in javascript numerically

I would like to sort an array of strings (in javascript) so that groups of numbers inside strings are compared as integers, not strings. I'm not worried about signed or floating point numbers.

for example, the result should be ["a1b3","a9b2","a10b2","a10b11"] not ["a1b3","a10b11","a10b2","a9b2"]

The easiest way to do this is to split each line into borders around groups of numbers. Is there a pattern that I can pass to String.split to split at the borders of characters without deleting any characters?

"abc11def22ghi".split(/?/) = ["abc","11","def","22","ghi"];

Or is there another way to compare strings that are not related to their splitting, perhaps filling all groups of digits with leading zeros so that they are the same length?

"aa1bb" => "aa00000001bb", "aa10bb" => "aa00000010bb"

I work with arbitrary strings, not strings that have a specific arrangement of groups of numbers.

Edit:

I like the /(\d+)/ one liner from Gaby to split the array. How compatible is this compatibility?

Solutions that parse strings once in a way that can be used to restore originals are much more efficient than this comparison function. None of the answers handles some lines, starting with numbers, while others do not, but that would be easy enough to fix and not be obvious in the original question.

 ["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"].sort( function ( inA , inB ) { var result = 0; var a , b , pattern = /(\d+)/; var as = inA.split( pattern ); var bs = inB.split( pattern ); var index , count = as.length; if ( ( '' === as[0] ) === ( '' === bs[0] ) ) { if ( count > bs.length ) count = bs.length; for ( index = 0 ; index < count && 0 === result ; ++index ) { a = as[index]; b = bs[index]; if ( index & 1 ) { result = a - b; } else { result = !( a < b ) ? ( a > b ) ? 1 : 0 : -1; } } if ( 0 === result ) result = as.length - bs.length; } else { result = !( inA < inB ) ? ( inA > inB ) ? 1 : 0 : -1; } return result; } ).toString(); 

result: "!!,9,9.5,10,a3,a3b,a3b3,a3b20,a3b100,a20,a100,~~"

+8
javascript sorting split regex natural-sort
source share
7 answers

I think it does what you want

 function sortArray(arr) { var tempArr = [], n; for (var i in arr) { tempArr[i] = arr[i].match(/([^0-9]+)|([0-9]+)/g); for (var j in tempArr[i]) { if( ! isNaN(n = parseInt(tempArr[i][j])) ){ tempArr[i][j] = n; } } } tempArr.sort(function (x, y) { for (var i in x) { if (y.length < i || x[i] < y[i]) { return -1; // x is longer } if (x[i] > y[i]) { return 1; } } return 0; }); for (var i in tempArr) { arr[i] = tempArr[i].join(''); } return arr; } alert( sortArray(["a1b3", "a10b11", "a10b2", "a9b2"]).join(",") ); 
+17
source share

Use this comparison function to sort.

 function compareLists(a,b){ var alist = a.split(/(\d+)/), // split text on change from anything to digit and digit to anything blist = b.split(/(\d+)/); // split text on change from anything to digit and digit to anything alist.slice(-1) == '' ? alist.pop() : null; // remove the last element if empty blist.slice(-1) == '' ? blist.pop() : null; // remove the last element if empty for (var i = 0, len = alist.length; i < len;i++){ if (alist[i] != blist[i]){ // find the first non-equal part if (alist[i].match(/\d/)) // if numeric { return +alist[i] - +blist[i]; // compare as number } else { return alist[i].localeCompare(blist[i]); // compare as string } } } return true; } 

Syntax

 var data = ["a1b3","a10b11","b10b2","a9b2","a1b20","a1c4"]; data.sort( compareLists ); alert(data); 

demo at http://jsfiddle.net/h9Rqr/7/

+6
source share

Assuming what you want to do, just enter a numerical sort by numbers in each array entry (ignoring the numbers), you can use this:

 function sortByDigits(array) { var re = /\D/g; array.sort(function(a, b) { return(parseInt(a.replace(re, ""), 10) - parseInt(b.replace(re, ""), 10)); }); return(array); } 

It uses a special sorting function that deletes numbers and converts them to a number every time it asks for a comparison. You can see how it works here: http://jsfiddle.net/jfriend00/t87m2/ .

If this is not what you want, then please clarify, since your question is not very clear regarding what kind should work.

+5
source share

Another option is to use an Intl.Collator instance with a numeric parameter:

 var array = ["a100","a20","a3","a3b","a3b100","a3b20","a3b3","!!","~~","9","10","9.5"]; var collator = new Intl.Collator(undefined, {numeric: true}); array.sort((a, b) => collator.compare(a, b)); console.log(array); 
+2
source share

Here's a more complete solution , which is sorted according to the letters and numbers in the lines

 function sort(list) { var i, l, mi, ml, x; // copy the original array list = list.slice(0); // split the strings, converting numeric (integer) parts to integers // and leaving letters as strings for( i = 0, l = list.length; i < l; i++ ) { list[i] = list[i].match(/(\d+|[az]+)/g); for( mi = 0, ml = list[i].length; mi < ml ; mi++ ) { x = parseInt(list[i][mi], 10); list[i][mi] = !!x || x === 0 ? x : list[i][mi]; } } // sort deeply, without comparing integers as strings list = list.sort(function(a, b) { var i = 0, l = a.length, res = 0; while( res === 0 && i < l) { if( a[i] !== b[i] ) { res = a[i] < b[i] ? -1 : 1; break; } // If you want to ignore the letters, and only sort by numbers // use this instead: // // if( typeof a[i] === "number" && a[i] !== b[i] ) { // res = a[i] < b[i] ? -1 : 1; // break; // } i++; } return res; }); // glue it together again for( i = 0, l = list.length; i < l; i++ ) { list[i] = list[i].join(""); } return list; } 
+1
source share

Sorting occurs from left to right if you have not created your own algorithm. Letters or numbers first compare numbers, then letters.

However, what you want to accomplish according to your own example (a1, a9, a10) DOES NOT CONTINUE. This will require that you know the data before hand and break the line in every way before applying sorting.

One of the latest options:

a) interrupt each line from left to right whenever this change is from letter to number and vice versa; & Amp; b) then start sorting by these groups from RIGHT-LEFT. This will be a very demanding algorithm. You can do it!

Finally, if you are the GENERATOR of the source β€œtext,” you should consider output NORMALIZATION, where a1 a9 a10 can be output as a01 a09 a10. That way you can have the full cotnrol final version of the algorithm.

Good luck

0
source share

I need a way to take a mixed string and create a string that could be sorted elsewhere so that the numbers are sorted by number and letters in alphabetical order. Based on the answers above, I created the following that fills all the numbers in a way that I can understand wherever they appear in the line.

 function padAllNumbers(strIn) { // Used to create mixed strings that sort numerically as well as non-numerically var patternDigits = /(\d+)/g; // This recognises digit/non-digit boundaries var astrIn = strIn.split( patternDigits ); // we create an array of alternating digit/non-digit groups var result = ""; for (var i=0;i<astrIn.length; i++) { if (astrIn[i] != "") { // first and last elements can be "" and we don't want these padded out if (isNaN(astrIn[i])) { result += astrIn[i]; } else { result += padOneNumberString("000000000",astrIn[i]); } } } return result; } function padOneNumberString(pad,strNum,left) { // Pad out a string at left (or right) if (typeof strNum === "undefined") return pad; if (typeof left === "undefined") left = true; var padLen = pad.length - (""+ strNum).length; var padding = pad.substr(0,padLen); return left? padding + strNum : strNum + padding; } 
0
source share

All Articles