Last active
July 24, 2024 10:05
-
-
Save anthonyec/ec86c518d9729c1e208a9fdc8e89e8de to your computer and use it in GitHub Desktop.
The Utils I Have Always Wanted dot TS
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
export function prepend<T>(array: T[], value: T): T[] { | |
return [value, ...array]; | |
} | |
export function append<T>(array: T[], value: T): T[] { | |
return [...array, value]; | |
} | |
export function insert<T>(array: T[], index: number, value: T): T[] { | |
return [...array.slice(0, index), value, ...array.slice(index, array.length)]; | |
} | |
export function replace<T>(array: T[], index: number, value: T): T[] { | |
return [...array.slice(0, index), value, ...array.slice(index + 1)]; | |
} | |
export function remove<T>(array: T[], index: number): T[] { | |
return [...array.slice(0, index), ...array.slice(index + 1)]; | |
} | |
export function first<T>(array: T[]): NonNullable<T> | undefined { | |
const item = array[0]; | |
if (item === undefined || item === null) return; | |
return item; | |
} | |
export function last<T>(array: T[]): NonNullable<T> | undefined { | |
const item = array[array.length - 1]; | |
if (item === undefined || item === null) return; | |
return item; | |
} | |
export function reverse<T>(array: T[]): T[] { | |
return [...array].reverse(); | |
} | |
export function sort<T>(array: T[], compare?: (a: T, b: T) => number): T[] { | |
return [...array].sort(compare); | |
} | |
export function previous<T>(array: T[], index: number): NonNullable<T> | undefined { | |
if (index === 0) return; | |
const item = array[index - 1]; | |
if (item === undefined || item === null) return; | |
return item; | |
} | |
export function next<T>(array: T[], index: number): NonNullable<T> | undefined { | |
if (index >= array.length - 1) return; | |
const item = array[index + 1]; | |
if (item === undefined || item === null) return; | |
return item; | |
} | |
export function forwards(iterations: number, length: number): number { | |
return iterations + 1; | |
} | |
export function backwards(iterations: number, length: number): number { | |
return length - 1 - iterations; | |
} | |
export function skip(every: number): IterationBehaviour { | |
return (iterations: number): number => { | |
return iterations * every; | |
}; | |
} | |
type IterationBehaviour = (iterations: number, length: number) => number; | |
export function iterate<T>(array: T[], behaviour: IterationBehaviour = forwards) { | |
return { | |
[Symbol.iterator]() { | |
let index = behaviour(-1, array.length); | |
let iterations = 0; | |
return { | |
next() { | |
if (iterations > array.length * 3) { | |
throw Error('Infinite loop protection'); | |
} | |
const value = { | |
index, | |
current: array[index], | |
previous: previous(array, index), | |
next: next(array, index), | |
isStart: index === 0, | |
isEnd: index === array.length - 1 | |
}; | |
if (index >= 0 && index < array.length) { | |
index = behaviour(iterations, array.length); | |
iterations += 1; | |
return { value, done: false }; | |
} | |
return { value: value, done: true }; | |
} | |
}; | |
} | |
}; | |
} |
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
export function assert(condition: unknown, ...message: unknown[]): asserts condition { | |
if (condition) return; | |
throw Error(`Assertion error: ${message.join(', ')}`); | |
} |
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
export type Range = [start: number, end: number]; | |
export function indicesOf(haystack: string, needle: string): number[] { | |
if (haystack.length === 0 || needle.length === 0) return []; | |
const indices: number[] = []; | |
let index = haystack.indexOf(needle); | |
while (index !== -1) { | |
indices.push(index); | |
index = haystack.indexOf(needle, index + needle.length); | |
} | |
return indices; | |
} | |
export function isLowerCase(text: string): boolean { | |
return text === text.toLowerCase() && text !== text.toUpperCase(); | |
} | |
export function isUpperCase(text: string): boolean { | |
return text !== text.toLowerCase() && text === text.toUpperCase(); | |
} | |
export function matchCase(original: string, template: string): string { | |
const maxIndex = Math.min(original.length, template.length); | |
let output = ''; | |
for (let index = 0; index < original.length; index++) { | |
const originalCharacter = original[index]; | |
if (index >= maxIndex) { | |
output += originalCharacter; | |
continue; | |
} | |
const templateCharacter = template[index]; | |
if (isUpperCase(templateCharacter)) { | |
output += originalCharacter.toUpperCase(); | |
continue; | |
} | |
output += originalCharacter; | |
} | |
return output; | |
} | |
export function replaceRange( | |
text: string, | |
replacement: string, | |
start: number, | |
end: number | |
): string { | |
const before = text.slice(0, start); | |
const after = text.slice(end); | |
return before + replacement + after; | |
} | |
export function replaceAllRanges( | |
text: string, | |
replacement: string, | |
ranges: Range[], | |
preserveCase?: boolean | |
) { | |
let result: string = text; | |
let offset: number = 0; | |
for (const [start, end] of ranges) { | |
const previousLength = result.length; | |
const offsetStart = start - offset; | |
const offsetEnd = end - offset; | |
result = replaceRange( | |
result, | |
preserveCase ? matchCase(replacement, result.slice(offsetStart, offsetEnd)) : replacement, | |
offsetStart, | |
offsetEnd | |
); | |
offset += previousLength - result.length; | |
} | |
return result; | |
} | |
export function pluralize(singular: string, plural: string, count: number) { | |
if (count === 1) return singular; | |
return plural; | |
} | |
export function findRanges( | |
text: string, | |
query: string, | |
isCaseSensitive: boolean, | |
isRegex: boolean | |
): Range[] { | |
if (!text || !query) return []; | |
const haystack = isCaseSensitive ? text : text.toLowerCase(); | |
const needle = isCaseSensitive ? query : query.toLowerCase(); | |
if (isRegex) { | |
const regex = new RegExp(needle, 'g'); | |
const matches = haystack.matchAll(regex); | |
const ranges: Range[] = []; | |
for (const match of matches) { | |
const start = match.index ?? 0; | |
const end = start + match[0].length; | |
ranges.push([start, end]); | |
} | |
return ranges; | |
} | |
const indices = indicesOf(haystack, needle); | |
if (indices.length === 0) return []; | |
const ranges: Range[] = []; | |
for (const start of indices) { | |
ranges.push([start, start + needle.length]); | |
} | |
return ranges; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment