@linkdotnet/stringoperations
Version:
Collection of string utilities. Edit-Distances, Search and Data structures. Offers for example trie, levenshtein distance.
84 lines (83 loc) • 3.64 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.findAll = exports.contains = void 0;
/**
* Checks whether 'text' has an occurrence of 'word'
* @param text Text to look for the occurrences of 'word'
* @param word Word to look for in 'text'
* @param ignoreCase Ignore case, when comparing each character
* @returns Returns true, if an occurence was found otherwise false
*/
function contains(text, word, ignoreCase = false) {
return findAll(text, word, ignoreCase, true) !== [];
}
exports.contains = contains;
/**
* Finds all occurences of 'word' in 'text' and returns the indexes
* @param text Text to look for the occurrences of 'word'
* @param word Word to look for in 'text'
* @param ignoreCase Ignore case, when comparing each character
* @param abortOnFirstOccurrence If set to true, findAll will only return the first index instead of all
* @returns Array of indexes. Empty if no occurrence was found
*/
function findAll(text, word, ignoreCase = false, abortOnFirstOccurrence = false) {
if (text.length === 0 || word.length === 0) {
return [];
}
if (text.length < word.length) {
return [];
}
const wordLength = word.length;
const textLength = text.length;
const badCharacterTable = createBadCharacterTable(word, ignoreCase);
let shift = 0;
const occurrences = [];
while (shift <= textLength - wordLength) {
let index = word.length - 1;
index = reduceIndexWhileMatchAtShift(text, word, ignoreCase, index, shift);
if (index < 0) {
occurrences.push(shift);
if (abortOnFirstOccurrence) {
break;
}
shift = shiftPatternToNextCharacterWithLastOccurrenceOfPattern(text, shift, wordLength, textLength, badCharacterTable, ignoreCase);
}
else {
shift = shiftPatternAfterBadCharacter(text, shift, index, badCharacterTable, ignoreCase);
}
}
return occurrences;
}
exports.findAll = findAll;
function createBadCharacterTable(text, ignoreCase) {
const alphabetSize = 256;
const table = new Array(alphabetSize).fill(-1);
for (let i = 0; i < text.length; i++) {
const character = ignoreCase ? text[i].toUpperCase() : text[i];
const asciiNumber = character.charCodeAt(0);
table[asciiNumber] = i;
}
return table;
}
function reduceIndexWhileMatchAtShift(text, word, ignoreCase, index, shift) {
while (index >= 0 && isCharacterEqual(text, word, ignoreCase, shift + index, index)) {
index--;
}
return index;
}
function isCharacterEqual(text, word, ignoreCase, positionInText, positionInWord) {
return ignoreCase ? text[positionInText].toUpperCase() === word[positionInWord].toUpperCase() : text[positionInText] === word[positionInWord];
}
function shiftPatternToNextCharacterWithLastOccurrenceOfPattern(text, shift, wordLength, textLength, badCharacterTable, ignoreCase) {
return shift + (shift + wordLength < textLength
? wordLength - badCharacterTable[getAsciiNumberFromText(text, shift + wordLength, ignoreCase)]
: 1);
}
function getAsciiNumberFromText(text, position, ignoreCase) {
const character = ignoreCase ? text[position].toUpperCase() : text[position];
return character.charCodeAt(0);
}
function shiftPatternAfterBadCharacter(text, shift, index, badCharacterTable, ignoreCase) {
const character = getAsciiNumberFromText(text, shift + index, ignoreCase);
return shift + Math.max(1, index - badCharacterTable[character]);
}