Last active
January 28, 2019 20:03
-
-
Save ericcornelissen/407f7d7f518feacbea7ff0348caeae76 to your computer and use it in GitHub Desktop.
JavaScript Array prototype method to sort strings containing numbers correctly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Sort an array of string alpha-numerically. | |
* | |
* @example | |
* // returns ['1', '2', '10', '20'] rather then ['1', '10', '2', '20'] | |
* ['1', '20', '10', '2'].strSort(); | |
* | |
* @return {array} The alpha-numerically sorted array. | |
* @license The-Unlicense | |
*/ | |
Array.prototype.strSort = function() { | |
var cache = { '': -Infinity /* makes sure empty strings always come first */ }; | |
var getStartNumber = function(str) { | |
// Return the cached value for the string if | |
// present. Check for `undefined` explicitly | |
// because the value can be `0` or `NaN`. | |
if(cache[str] !== undefined) { | |
return cache[str]; | |
} | |
// Check for tabs/spaces at the start of the input | |
// string and return special values accordingly. | |
var char = str.charAt(0); | |
if(/\s/.test(char)) { | |
// Ensure spaces (`-1`) come after tabs (`-2`). | |
return char === ' ' ? -1 : -2 | |
} | |
// Initialize a counter to keep track of what | |
// character in the string is being evaluated. | |
var i = -1; | |
// Initialize a string to rebuild to input string. | |
var substr = ''; | |
// Create a variable to remember the last valid | |
// number that was found. Initialized to NaN for | |
// strings that don't start with a number. | |
var valid = NaN; | |
// Loop over the input string to find the number | |
// it starts with. | |
while(i++, i < str.length) { | |
// Update the substring with the next character | |
// in the string. | |
substr += str.charAt(i); | |
// Parse the substring to a number. | |
var num = Number(substr); | |
// Check if the number (`num`) is still a number. | |
if(isNaN(num)) { | |
break; | |
} else { | |
// Update the last valid number found, since | |
// the current substring is a valid number. | |
valid = num; | |
} | |
} | |
// Cache the number found for the string. | |
cache[str] = valid; | |
// Return the last valid number found. | |
return valid; | |
}; | |
var compare = function(a, b) { | |
var aNum = getStartNumber(a), | |
bNum = getStartNumber(b); | |
// `aNum - bNum` is NaN whenever aNum || bNum is | |
// NaN. Otherwise both aNum and bNum are numbers. | |
if(isNaN(aNum - bNum)) { | |
// Given that `aNum - bNum` is NaN, then if | |
// `aNum` is not NaN, `bNum` must be NaN and | |
// so a < b. | |
if(!isNaN(aNum)) { | |
return -1; | |
} | |
// Given that `aNum - bNum` is NaN, then if | |
// `bNum` is not NaN, `aNum` must be NaN and | |
// so a > b. | |
if(!isNaN(bNum)) { | |
return 1; | |
} | |
// --> Continues to [1] | |
} else if(aNum === bNum) { | |
// When the numbers are negative, the original | |
// strings A and B started with spaces/tabs and | |
// the strings should be compared without them. | |
if(aNum < 0) { | |
return compare(a.substr(1), b.substr(1)); | |
} | |
// Otherwise, Find the textual length of the | |
// number and get the remaining substrings for | |
// both of the input strings. | |
var numLength = String(aNum).length; | |
a = a.substr(numLength); | |
b = b.substr(numLength); | |
// --> Continues to [1] | |
} else { | |
// Otherwise, compare aNum and bNum using | |
// the regular integer comparison method. | |
return aNum - bNum; | |
} | |
// Otherwise, compare the two strings using the | |
// regular string comparison method. [1] | |
return a < b ? -1 : a > b ? 1 : 0; | |
} | |
return this.sort(compare); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment