Skip to content

Commit 3eabc73

Browse files
author
Jim Palmer
committed
version 0.8.1
+ added better unicode locale support fixing #9 + added regex lookahead for better chunking with mixed number/strings + fix for #13 (comment)
1 parent 71044f7 commit 3eabc73

2 files changed

Lines changed: 41 additions & 25 deletions

File tree

naturalSort.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/*
2-
* Natural Sort algorithm for Javascript - Version 0.8 - Released under MIT license
2+
* Natural Sort algorithm for Javascript - Version 0.8.1 - Released under MIT license
33
* Author: Jim Palmer (based on chunking idea from Dave Koelle)
44
*/
55
function naturalSort (a, b) {
6-
var re = /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[\da-fA-F]+$|\d+)/g,
6+
var re = /(^([+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|^0x[\da-fA-F]+$|\d+)/g,
77
sre = /^\s+|\s+$/g, // trim pre-post whitespace
88
snre = /\s+/g, // normalize all whitespace to single ' ' character
99
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
@@ -13,8 +13,8 @@ function naturalSort (a, b) {
1313
return (naturalSort.insensitive && ('' + s).toLowerCase() || '' + s).replace(sre, '');
1414
},
1515
// convert all to strings strip whitespace
16-
x = i(a) || '',
17-
y = i(b) || '',
16+
x = i(a),
17+
y = i(b),
1818
// chunk/tokenize
1919
xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
2020
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
@@ -28,22 +28,23 @@ function naturalSort (a, b) {
2828
oFxNcL, oFyNcL;
2929
// first try and sort Hex codes or Dates
3030
if (yD) {
31-
if ( xD < yD ) { return -1; }
32-
else if ( xD > yD ) { return 1; }
31+
if (xD < yD) { return -1; }
32+
else if (xD > yD) { return 1; }
3333
}
3434
// natural sorting through split numeric strings and default strings
35-
for(var cLoc=0, xNl = xN.length, yNl = yN.length, numS=Math.max(xNl, yNl); cLoc < numS; cLoc++) {
35+
for(var cLoc = 0, xNl = xN.length, yNl = yN.length, numS = Math.max(xNl, yNl); cLoc < numS; cLoc++) {
3636
oFxNcL = normChunk(xN[cLoc] || '', xNl);
3737
oFyNcL = normChunk(yN[cLoc] || '', yNl);
3838
// handle numeric vs string comparison - number < string - (Kyle Adams)
39-
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) { return (isNaN(oFxNcL)) ? 1 : -1; }
40-
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
41-
else if (typeof oFxNcL !== typeof oFyNcL) {
42-
oFxNcL += '';
43-
oFyNcL += '';
39+
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
40+
return isNaN(oFxNcL) ? 1 : -1;
41+
}
42+
// if unicode use locale comparison
43+
if (/[^\x00-\x80]/.test(oFxNcL + oFyNcL) && oFxNcL.localeCompare) {
44+
var comp = oFxNcL.localeCompare(oFyNcL);
45+
return comp / Math.abs(comp);
4446
}
4547
if (oFxNcL < oFyNcL) { return -1; }
46-
if (oFxNcL > oFyNcL) { return 1; }
48+
else if (oFxNcL > oFyNcL) { return 1; }
4749
}
48-
return 0;
4950
}

unit-tests.html

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -345,16 +345,18 @@
345345
['The Wind in the Willows', 'The 40th step more', 'The 39 steps', 'Wanda'],
346346
['The 39 steps', 'The 40th step more', 'The Wind in the Willows', 'Wanda'],
347347
'Title sorts');
348+
348349
naturalSort.insensitive = true;
349350
wrapTest(
350351
['Equiv. \xfd accents: 2-2', 'Equiv. \xdd accents: 2-1', 'Equiv. y accents: 2+0', 'Equiv. Y accents: 2+1'],
351-
['Equiv. y accents: 2+0', 'Equiv. Y accents: 2+1', 'Equiv. \xdd accents: 2-1', 'Equiv. \xfd accents: 2-2'],
352+
['Equiv. y accents: 2+0', 'Equiv. Y accents: 2+1', 'Equiv. \xfd accents: 2-2', 'Equiv. \xdd accents: 2-1'],
352353
'Equivalent accented characters (and case) (naturalSort.insensitive = true)');
353354
naturalSort.insensitive = false;
354-
wrapTest(
355-
['Start with an \u0292: 2-2', 'Start with an \u017f: 2-1', 'Start with an \xdf: 2+0', 'Start with an s: 2+1'],
356-
['Start with an s: 2+1', 'Start with an \xdf: 2+0', 'Start with an \u017f: 2-1', 'Start with an \u0292: 2-2'],
357-
'Character replacements');
355+
// This is not a valuable unicode ordering test
356+
// wrapTest(
357+
// ['Start with an \u0292: 2-2', 'Start with an \u017f: 2-1', 'Start with an \xdf: 2+0', 'Start with an s: 2+1'],
358+
// ['Start with an s: 2+1', 'Start with an \xdf: 2+0', 'Start with an \u017f: 2-1', 'Start with an \u0292: 2-2'],
359+
// 'Character replacements');
358360
});
359361
test('contributed tests', function () {
360362
wrapTest(
@@ -420,15 +422,15 @@
420422
['1', '02', '3'],
421423
['1', '02', '3'],
422424
'issue #18 - Any zeros that precede a number messes up the sorting - menixator');
423-
// all strings are interpreted as floats and sorted accordingly - they are not chunked
424-
// wrapTest(
425-
// ['1.100', '1.1', '1.10', '1.54'],
426-
// ['1.1', '1.10', '1.54', '1.100'],
427-
// "issue #13 - ['1.100', '1.10', '1.1', '1.54'] etc do not sort properly... - rubenstolk");
425+
// strings are coerced as floats/ints if possible and sorted accordingly - e.g. they are not chunked
426+
wrapTest(
427+
['1.100', '1.1', '1.10', '1.54'],
428+
['1.1', '1.10', '1.54', '1.100'],
429+
"issue #13 - ['1.100', '1.10', '1.1', '1.54'] etc do not sort properly... - rubenstolk");
428430
wrapTest(
429431
['v1.100', 'v1.1', 'v1.10', 'v1.54'],
430432
['v1.1', 'v1.10', 'v1.54', 'v1.100'],
431-
"issue #13 - ['1.100', '1.10', '1.1', '1.54'] etc do not sort properly... - rubenstolk");
433+
"issue #13 - ['v1.100', 'v1.10', 'v1.1', 'v1.54'] etc do not sort properly... - rubenstolk (bypass float coercion)");
432434
wrapTest(
433435
[
434436
'MySnmp 1234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567',
@@ -451,6 +453,19 @@
451453
['SomeString', 'SomeString 1'],
452454
['SomeString', 'SomeString 1'],
453455
"PR #19 - ['SomeString', 'SomeString 1'] bombing on 'undefined is not an object' - dannycochran");
456+
wrapTest(
457+
['Udet', '\xDCbelacker', 'Uell', '\xDClle', 'Ueve', '\xDCxk\xFCll', 'Uffenbach'],
458+
['\xDCbelacker', 'Udet', 'Uell', 'Ueve', 'Uffenbach', '\xDClle', '\xDCxk\xFCll'],
459+
"issue #9 - Sorting umlauts characters \xC4, \xD6, \xDC - diogoalves");
460+
wrapTest(
461+
['2.2 sec', '1.9 sec', '1.53 sec'],
462+
['1.53 sec', '1.9 sec', '2.2 sec'],
463+
"https://github.com/overset/javascript-natural-sort/issues/13 - ['2.2 sec','1.9 sec','1.53 sec'] - padded by spaces - harisb");
464+
wrapTest(
465+
['2.2sec', '1.9sec', '1.53sec'],
466+
['1.53sec', '1.9sec', '2.2sec'],
467+
"https://github.com/overset/javascript-natural-sort/issues/13 - ['2.2sec','1.9sec','1.53sec'] - no padding - harisb");
468+
454469
});
455470

456471
});

0 commit comments

Comments
 (0)