UNPKG

@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
"use strict"; 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]); }