Skip to content

Instantly share code, notes, and snippets.

@icfantv
Created May 13, 2016 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save icfantv/775a6000748384e9f77569017d67c240 to your computer and use it in GitHub Desktop.
Save icfantv/775a6000748384e9f77569017d67c240 to your computer and use it in GitHub Desktop.
Alpha-Numeric comparator in TypeScript
export class StringUtils {
// shamelessly ripped from http://www.davekoelle.com/alphanum.html
/**
* Returns whether or not the specified value is a string.
* @param value the value to check for a string type.
* @returns {boolean} whether or not the specified value is a string.
*/
public static isString(value: any): boolean {
return typeof value === 'string';
}
/**
* Returns a number that indicates the order relationship between the two specified strings.
* @param s1 {string} the first string
* @param s2 the second string
* @param caseInsensitive {boolean} whether or not the strings should be compared in a
* case-insensitive manner. <em>Default: <code>true</code></em>
* @returns {number} a negative number of the first string should appear before the second,
* zero (0) if the first string and second string are equivalent, and a positive number if
* the first string should appear after the second.
*/
public static compare(s1: string, s2: string, caseInsensitive = true): number {
if (!StringUtils.isString(s1) || !StringUtils.isString(s2)) {
return 0;
}
if (caseInsensitive) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
}
let thisMarker = 0;
let thatMarker = 0;
let s1Length: number = s1.length;
let s2Length: number = s2.length;
while (thisMarker < s1Length && thatMarker < s2Length) {
let thisChunk: string = StringUtils.getChunk(s1, s1Length, thisMarker);
thisMarker += thisChunk.length;
let thatChunk: string = StringUtils.getChunk(s2, s2Length, thatMarker);
thatMarker += thatChunk.length;
// if both chunks contain numeric characters, sort them numerically
let result = 0;
if (StringUtils.isDigit(thisChunk[0]) && StringUtils.isDigit(thatChunk[0])) {
// simple chunk comparison by length
let thisChunkLength = thisChunk.length;
result = thisChunkLength - thatChunk.length;
// if equal, the first different number counts
if (result === 0) {
for (let i = 0; i < thisChunkLength; i++) {
result = thisChunk.charCodeAt(i) - thatChunk.charCodeAt(i);
if (result !== 0) {
return result;
}
}
}
}
else {
result = thisChunk.localeCompare(thatChunk);
}
if (result !== 0) {
return result;
}
}
return s1Length - s1Length;
}
private static isDigit(ch: string): boolean {
return ch.charCodeAt(0) >= 48 && ch.charCodeAt(0) <= 57;
}
// length of string is passed in for improved efficiency (only need to calculate it once)
private static getChunk(s: string, slength: number, marker: number): string {
const chunk: Array<string> = [];
let c = s[marker];
chunk.push(c);
marker++;
if (StringUtils.isDigit(c)) {
while (marker < slength) {
c = s[marker];
if (!StringUtils.isDigit(c)) {
break;
}
chunk.push(c);
marker++;
}
}
return chunk.join('');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment