Sort Array of numeric & alphabetical elements (Natural Sort)

Suppose I have an array

``````var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
``````

and I try sorting it, I get something like ...

``````[1, 1, 10, 2, 2, 3, 5, 55, 7, 75, 8, "abc", "ahsldk", "huds"]
``````

notice 10 is before 2, how can I have something more like

``````[1,1,2,2,3,5 ..., "abc", "ahs...",...]
``````

``````Array.prototype.naturalSort= function(){
var a, b, a1, b1, rx=/(\d+)|(\D+)/g, rd=/\d+/;
return this.sort(function(as, bs){
a= String(as).toLowerCase().match(rx);
b= String(bs).toLowerCase().match(rx);
while(a.length && b.length){
a1= a.shift();
b1= b.shift();
if(rd.test(a1) || rd.test(b1)){
if(!rd.test(a1)) return 1;
if(!rd.test(b1)) return -1;
if(a1!= b1) return a1-b1;
}
else if(a1!= b1) return a1> b1? 1: -1;
}
return a.length- b.length;
});
}
``````

Or, from Alphanum: Javascript Natural Sorting Algorithm by Brian Huisman:

``````Array.prototype.alphanumSort = function(caseInsensitive) {
for (var z = 0, t; t = this[z]; z++) {
this[z] = [];
var x = 0, y = -1, n = 0, i, j;

while (i = (j = t.charAt(x++)).charCodeAt(0)) {
var m = (i == 46 || (i >=48 && i <= 57));
if (m !== n) {
this[z][++y] = "";
n = m;
}
this[z][y] += j;
}
}

this.sort(function(a, b) {
for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) {
if (caseInsensitive) {
aa = aa.toLowerCase();
bb = bb.toLowerCase();
}
if (aa !== bb) {
var c = Number(aa), d = Number(bb);
if (c == aa && d == bb) {
return c - d;
} else return (aa > bb) ? 1 : -1;
}
}
return a.length - b.length;
});

for (var z = 0; z < this.length; z++)
this[z] = this[z].join("");
}
``````

Short and sweet, per the original question:

``````var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
arr.sort(function(a,b){
var a1=typeof a, b1=typeof b;
return a1<b1 ? -1 : a1>b1 ? 1 : a<b ? -1 : a>b ? 1 : 0;
});
// [1, 1, 2, 2, 3, 5, 7, 8, 10, 55, 75, "abc", "ahsldk", "huds"]
``````

(Sort first by type, then by value.)

A more-full-featured natural sort:

``````var items = ['a1c', 'a01', 'a1', 'a13', 'a1a', 'a1b', 'a3b1', 'a1b0',
'a1b3', 'a1b1', 'dogs', 'cats', 'hogs', 'a2', '2', '20',
1, 13, 1.1, 1.13, '1.2', 'a'];

console.log(naturalSort(items))

function naturalSort(ary, fullNumbers) {
var re = fullNumbers ? /[\d\.\-]+|\D+/g : /\d+|\D+/g;

// Perform a Schwartzian transform, breaking each entry into pieces first
for (var i=ary.length;i--;)
ary[i] = [ary[i]].concat((ary[i]+"").match(re).map(function(s){
return isNaN(s) ? [s,false,s] : [s*1,true,s];
}));

// Perform a cascading sort down the pieces
ary.sort(function(a,b){
var al = a.length, bl=b.length, e=al>bl?al:bl;
for (var i=1;i<e;++i) {
// Sort "a" before "a1"
if (i>=al) return -1; else if (i>=bl) return 1;
else if (a[i][0]!==b[i][0])
return (a[i][1]&&b[i][1]) ?        // Are we comparing numbers?
(a[i][0]-b[i][0]) :         // Then diff them.
(a[i][2]<b[i][2]) ? -1 : 1; // Otherwise, lexicographic sort
}
return 0;
});

// Restore the original values into the array
for (var i=ary.length;i--;) ary[i] = ary[i][0];
return ary;
}``````

With `naturalSort`, pass `true` as the second parameter if you want "1.13" to sort before "1.2".

// Most natural sorts are for sorting strings, so `file2` is sorted before `file10`.

If you are mixing in actual numbers you need to sort them to the front of the array, because negative numbers and digits separated by hyphens are a pain to interpret. Strings with leading zeroes need to be careful, so `part002` will sort before `part010`.

``````var natSort=function(as, bs) {
var a, b, a1, b1,
rx=  /(\d+)|(\D+)/g, rd= /\d/, rz=/^0/;
if(typeof as=='number' || typeof bs=='number'){
if(isNaN(as))return 1;
if(isNaN(bs))return -1;
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.match(rx);
b= b.match(rx);
while(a.length && b.length){
a1= a.shift();
b1= b.shift();
if(a1!== b1){
if(rd.test(a1) && rd.test(b1)){
return a1.replace(rz,'.0')- b1.replace(rz,'.0');
}
else return a1> b1? 1: -1;
}
}
return a.length - b.length;
}

array.sort(natSort)
``````

This is a refined.

``````var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"56","abc","huds"];
arr.sort(
function (a,b){
if ( isNaN(a)&&isNaN(b)) return a<b?-1:a==b?0:1;//both are string
else if (isNaN(a)) return 1;//only a is a string
else if (isNaN(b)) return -1;//only b is a string
else return a-b;//both are num
}
);
``````

result: 1|1|2|2|3|5|7|8|10|55|56|75|abc|ahsldk|huds|

If you have only alphabetical and integer items, you can stick with simple code:

``````var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
arr.sort(function(a, b)
{
if (a == b)
return 0;

var n1 = parseInt(a, 10);
var n2 = parseInt(b, 10);
if (isNaN(n1) && isNaN(n2)) {
//both alphabetical
return (a > b) ? 1 : 0;
}
else if (!isNaN(n1) && !isNaN(n2)) {
//both integers
return (n1 > n2) ? 1 : 0;
}
else if (isNaN(n1) && !isNaN(n2)) {
//a alphabetical and b is integer
return 1;
}

//a integer and b is alphabetical
return 0;
});
``````

Working example: http://jsfiddle.net/25X2e/

If you can always assume numbers and strings of unmixed alphas, I would just divide and conquer. slice out numbers into a new array using typeof. Sort both independently and then just join the two arrays.

I knew the following way also which might sort the array alphanumerically order.

``````const arr = [1, 5, "ahsldk", 10, 55, 3, 2, 7, 8, 1, 2, 75, "abc", "huds"];
arr.sort((a, b) => a - b || a.toString().localeCompare(b.toString()));
console.log(arr)``````

You could do this in one line using `String.prototype.localCompare()` and get the result you are looking for. Note that the numeric collation option is enabled.

``````var arr = [1,5,"ahsldk",10,55,3,2,7,8,1,2,75,"abc","huds"];
arr.sort((a,b) => ("" + a).localeCompare(b, undefined, {numeric: true}));
console.log(arr);
// [1, 1, 2, 2, 3, 5, 7, 8, 10, 55, 75, "abc", "ahsldk", "huds"]``````

Maybe add some logic to handle nulls.