JSFiddle - React, Tailwind, and code Playground
HTML
<h1>Natural JS Sorter</h1>
<p>The following sorts strings that are mixed with numbers by actually factoring in the value of the number s.t. the sort order reflects that numbers value. Think 'natural sort'.</p>
<p>If asked to sort the following:</p>
<ol>
<li>Week 1</li>
<li>Week 10</li>
<li>Week 2</li>
</ol>
<p>A normal js array sort will output results in THAT order. This sorter actually places the numbers in order. Play with the test data!</p>
<div class="log">
<h3>JS array.sort() w/ custom sorter Log</h3>
<ul id="log1"></ul>
</div>
<br/>
<br/>
<div class="log">
<h3>Standard JS sorting (no adds) Log</h3>
<ul id="log2"></ul>
</div>
CSS
.log {
background-color:#FFEAB6;
border-radius:5px;
font-family:"Helvetica", Arial;
padding: 10px;
}
JavaScript
function post(txt, logNum) {
var newItem = document.createElement("li");
newItem.innerHTML += txt;
document.getElementById('log' + logNum).appendChild(newItem);
}
// split @numbers credit: http://jsfiddle.net/5WJ9v/
var items = [
"Week 1",
"27str",
"22str",
"2str",
3,
"Week 10",
"Week 2",
0,
"Week 103 POSTFIX-JUNK 2",
"Week 103 POSTFIX-JUNK 1"];
var items2 = items.slice(0);
items.sort(naturalCompare).forEach(function(val){post(val,1);});
console.dir(items2);
items2.sort().forEach(function(val){post(val,2);});
// Custom compare func
function naturalCompare(a, b) {
if (!isNaN(a) && !isNaN(b)) return a - b;
else if (!isNaN(a) && isNaN(b)) return -1;
else if (isNaN(a) && !isNaN(b)) return 1;
else if (typeof a === "string" && typeof b === "string") {
var regex = /(\d+)/g,
aSplit = a.split(regex),
bSplit = b.split(regex),
aBlock, bBlock,
aBlockNum, bBlockNum;
for (var ndx = 0; ndx < aSplit.length; ++ndx) {
if (bSplit[ndx] !== undefined) {
aBlock = aSplit[ndx];
bBlock = bSplit[ndx];
} else {
return 1; // b is shorter, thus put it first
}
if (aBlock !== bBlock) {
// Check & compare numeric
aBlockNum = parseInt(aBlock, 10);
bBlockNum = parseInt(bBlock, 10);
if (aBlockNum && bBlockNum) return aBlockNum - bBlockNum;
if (aBlockNum && !bBlockNum) return -1;
if (!aBlockNum && bBlockNum) return 1;
// String compare
if (aBlock < bBlock) return -1;
if (aBlock > bBlock) return 1;
// if blocks ===, proceed to next block
}
}
return 0; // strings were equal
} else {
throw new Error("Invalid data passed to be sorted!");
}
};