Skip to content

Instantly share code, notes, and snippets.

@hashd
Last active August 12, 2022 23:24
Show Gist options
  • Save hashd/1fd8245483a1dab616c50215dae6a2c0 to your computer and use it in GitHub Desktop.
Save hashd/1fd8245483a1dab616c50215dae6a2c0 to your computer and use it in GitHub Desktop.
Casing Utilities - TypeScript
enum CharType {
NUMBER,
LOWERCASE_CHARACTER,
UPPERCASE_CHARACTER,
WHITESPACE,
OTHER,
}
type TRange = {
length: number;
ranges: [number, number][];
};
function classifyChar(ch): CharType {
if (ch >= "a" && ch <= "z") {
return CharType.LOWERCASE_CHARACTER;
}
if (ch >= "A" && ch <= "Z") {
return CharType.UPPERCASE_CHARACTER;
}
if (ch >= "0" && ch <= "9") {
return CharType.NUMBER;
}
if (["-", "_", "\t", ".", "\n", "\r", " "].indexOf(ch) >= 0) {
return CharType.WHITESPACE;
}
return CharType.OTHER;
}
function genericSplit(str: string) {
const charTypes = [];
for (let i = 0; i < str.length; i++) {
charTypes.push(classifyChar(str.charAt(i)));
}
function resultPlusNew(result: TRange, start: number, end: number) {
if (end > start) {
result.ranges.push([start, end]);
result.length = result.length + (end - start);
}
}
function loop(result: TRange, start: number, current: number) {
const next = current + 1;
if (current >= str.length) {
resultPlusNew(result, start, current);
return;
} else if (charTypes[current] === CharType.WHITESPACE) {
resultPlusNew(result, start, current);
return loop(result, next, next);
} else {
const currCharType = charTypes[current];
const nextCharType = charTypes[current + 1];
const skipLevelCharType = charTypes[current + 2];
if (
currCharType === CharType.UPPERCASE_CHARACTER &&
nextCharType === CharType.UPPERCASE_CHARACTER &&
skipLevelCharType === CharType.LOWERCASE_CHARACTER
) {
resultPlusNew(result, start, next);
return loop(result, next, next);
} else if (
nextCharType === CharType.UPPERCASE_CHARACTER &&
currCharType !== CharType.UPPERCASE_CHARACTER
) {
resultPlusNew(result, start, next);
return loop(result, next, next);
} else if (
nextCharType === CharType.NUMBER &&
currCharType !== CharType.NUMBER
) {
resultPlusNew(result, start, next);
return loop(result, next, next);
} else {
return loop(result, start, next);
}
}
}
const res: TRange = {
ranges: [],
length: 0,
};
loop(res, 0, 0);
return res;
}
function genericToCase(
str: string,
firstCharMutateFn,
restMutateFn,
joinStr = ""
): string {
const result = genericSplit(str);
let length = result.length;
if (length === 0) {
return "";
}
const ranges = result.ranges;
const nParts = ranges.length;
switch (joinStr) {
case undefined:
case null:
break;
default:
length = length + nParts - 1;
}
const finalParts = [];
ranges.reduce((pos, [start, end]) => {
let i = pos;
if (joinStr && i > 0) {
finalParts[i] = joinStr;
pos = pos + 1;
}
i = pos;
let char = str.charAt(start);
char = firstCharMutateFn(i, char);
finalParts[i] = char;
pos = pos + 1;
for (i = start + 1; i < end; i = i + 1) {
finalParts.push(restMutateFn(str.charAt(i)));
pos = pos + 1;
}
return pos;
}, 0);
// logger.info(
// ranges.map(([start, end]) => str.substring(start, end)).join(", ")
// );
return finalParts.join(joinStr);
}
export function camelCase(str: string): string {
return genericToCase(
str,
(idx, char) => (idx === 0 ? char.toLowerCase() : char.toUpperCase()),
(char) => char.toLowerCase()
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment