@thirteen-13/string-utility
Version:
string utility function
490 lines (489 loc) • 23.6 kB
JavaScript
export const isString = (str) => typeof str === 'string' || str instanceof String;
export const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
export const toCamelCase = (str) => {
if (/^[a-z][a-zA-Z0-9]*$/.test(str))
return str;
return str
.toLowerCase()
.replace(/[^a-zA-Z0-9]+(\w)/g, (_, c) => c.toUpperCase())
.replace(/^[^a-zA-Z0-9]*([a-zA-Z])/, (_, c) => c.toLowerCase());
};
export const toKebabCase = (str) => str.trim() // Trim any leading or trailing spaces
.replace(/([a-z])([A-Z])/g, '$1-$2') // Add hyphen between lowercase and uppercase letters
.replace(/(\d)([a-zA-Z])/g, '$1-$2') // Add hyphen between numbers and letters
.replace(/[\s_]+/g, '-') // Replace spaces and underscores with hyphens
.toLowerCase(); // Convert all to lowercase
export const toSnakeCase = (str) => str
.replace(/([a-z])([A-Z])/g, '$1_$2')
.replace(/(\d)([a-zA-Z])/g, '$1_$2')
.replace(/[\s-]+/g, '_')
.toLowerCase();
export const reverse = (str) => str.split('').reverse().join('');
export const truncate = (str, length) => {
const graphemes = Array.from(str);
return graphemes.length > length ? graphemes.slice(0, length).join('') + '…' : str;
};
export const stripHtml = (str) => {
const withoutScriptsAndStyles = str
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '')
.replace(/<style[\s\S]*?>[\s\S]*?<\/style>/gi, '');
return withoutScriptsAndStyles.replace(/<\/?[a-z][\s\S]*?>/gi, '');
};
export const escapeHtml = (str) => str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
export const isUpperCase = (str) => str === str.toUpperCase();
export const isLowerCase = (str) => str === str.toLowerCase();
export const repeat = (str, count) => {
return str.repeat(Math.floor(count));
};
export const padLeft = (str, length, char = ' ') => {
const fill = Array.from(char);
return str.length >= length ? str : fill[0]?.repeat(length - str.length) + str;
};
export const padRight = (str, length, char = ' ') => {
const fill = Array.from(char);
return str.length >= length ? str : str + fill[0]?.repeat(length - str.length);
};
export const contains = (str, substr) => str.includes(substr);
export const startsWith = (str, prefix) => str.startsWith(prefix);
export const endsWith = (str, suffix) => str.endsWith(suffix);
export const removeNonAlpha = (str) => str.replace(/[^a-z]/gi, '');
export const removeNonNumeric = (str) => str.replace(/[^0-9]/g, '');
export const removeWhitespace = (str) => str.replace(/\s/g, '');
export const countOccurrences = (str, sub) => {
if (sub === "") {
return str?.length + 1;
}
return str.split(sub).length - 1;
};
export const slugify = (str) => str
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
export const getInitials = (str) => str
.split(' ')
.map((word) => word[0])
.join('')
.toUpperCase();
export const isStrictPalindrome = (str) => str === str.split('').reverse().join('');
export const isLoosePalindrome = (str) => {
const cleaned = str.replace(/[^a-z0-9]/gi, '').toLowerCase();
return cleaned === cleaned.split('').reverse().join('');
};
export const extractNumbers = (str) => (str.match(/\d+/g) || []).map((value) => parseInt(value));
export const extractWords = (str) => str.match(/\b\w+\b/g) || [];
export const maskString = (str, start, end, maskChar = '*') => str
.split('')
.map((char, i) => (i >= start && i < end ? maskChar : char))
.join('');
export const randomString = (length) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join('');
};
export const isAlpha = (str) => /^[A-Za-z]+$/.test(str);
export const isAlphanumeric = (str) => /^[A-Za-z0-9]+$/.test(str);
export const isEmail = (str) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
export const extractEmails = (str) => str.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [];
export const extractUrls = (str) => str.match(/https?:\/\/[^\s]+[a-zA-Z0-9]/g) || [];
export const titleCase = (str) => {
const leadingSpaces = str.match(/^(\s*)/)?.[0] || '';
const trailingSpaces = str.match(/(\s*)$/)?.[0] || '';
const words = str.trim().split(/\s+/);
const titled = words
.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
.join(' ');
return leadingSpaces + titled + trailingSpaces;
};
export const swapCase = (str) => str
.split('')
.map((c) => c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase())
.join('');
export const removeDuplicateWords = (str) => Array.from(new Set(str.split(' ').filter(value => !!value))).join(" ");
export const safeString = (str) => str.replace(/[^\w\s]/gi, '');
export const compressWhitespace = (str) => str.replace(/\s+/g, ' ').trim();
export const charFrequency = (str) => Array.from(str).reduce((acc, char) => {
acc[char] = (acc[char] || 0) + 1;
return acc;
}, {});
export const levenshteinDistance = (a, b) => {
const m = a.length;
const n = b.length;
if (m === 0)
return n;
if (n === 0)
return m;
const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
for (let i = 0; i <= m; i++)
dp[i][0] = i;
for (let j = 0; j <= n; j++)
dp[0][j] = j;
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
dp[i][j] = Math.min(dp[i - 1][j] + 1, // deletion
dp[i][j - 1] + 1, // insertion
dp[i - 1][j - 1] + cost // substitution
);
}
}
return dp[m][n];
};
export const toPascalCase = (str) => str
.replace(/[^a-zA-Z0-9]+/g, ' ') // turn all non-alphanum into space
.trim()
.split(' ')
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
export const toDotCase = (str) => str.trim().replace(/\s+/g, '.').toLowerCase();
export const toSpaceCase = (str) => str.replace(/[_-]+/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2');
export const endsWithAny = (str, suffixes) => suffixes.some(suffix => str.endsWith(suffix));
export const startsWithAny = (str, prefixes) => prefixes.some(prefix => str.startsWith(prefix));
export const trimChar = (str, char) => {
const escapedChar = char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return str.replace(new RegExp(`^${escapedChar}+|${escapedChar}+$`, 'g'), '');
};
export const removeDiacritics = (str) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
export const getUniqueCharacters = (str) => Array.from(new Set(str.split('')));
export const stringToCharCodeArray = (str) => {
const codes = [];
for (const char of str) {
codes.push(char.codePointAt(0));
}
return codes;
};
export const charCodeArrayToString = (arr) => arr.map(code => String.fromCodePoint(code)).join('');
export const wrap = (str, wrapper) => `${wrapper}${str}${wrapper}`;
export const ensureStartsWith = (str, prefix) => str.startsWith(prefix) ? str : prefix + str;
export const ensureEndsWith = (str, suffix) => str.endsWith(suffix) ? str : str + suffix;
export const repeatStringUntilLength = (str, targetLength) => str.repeat(Math.ceil(targetLength / (str?.length || 1))).slice(0, targetLength);
export const collapseNewlines = (str) => str.replace(/[\r\n]+/g, '\n');
export const stringToAsciiSum = (str) => [...str].reduce((sum, char) => sum + char.codePointAt(0), 0);
export const getCharAtSafe = (str, index) => str[index] ?? '';
export const isWhitespace = (str) => /^\s*$/.test(str);
export const isEmpty = (str) => str?.length === 0;
export const isBlank = (str) => str.trim().length === 0;
export const getNthWord = (str, n) => str.split(/\s+/)[n] ?? '';
export const countVowels = (str) => (str.match(/[aeiou]/gi) || []).length;
export const countConsonants = (str) => (str.match(/[bcdfghjklmnpqrstvwxyz]/gi) || []).length;
export const stripPunctuation = (str) => str.replace(/[.!@#$%^&*()\-_=+{}\[\]:;"\/\\,~]/g, '');
export const extractHashtags = (str) => str.match(/#[\w]+/g) || [];
export const extractMentions = (str) => str.match(/@[\w]+/g) || [];
export const hasRepeatedCharacters = (str) => /(.)\1/.test(str);
export const isHexColor = (str) => /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(str);
export const isRgbColor = (str) => /^rgb\((\d{1,3},\s*){2}\d{1,3}\)$/.test(str);
export const getLastNChars = (str, n) => {
if (n === 0)
return "";
return str.slice(-n);
};
export const getFirstNChars = (str, n) => str.slice(0, n);
export const containsAny = (str, items) => items.some(item => str.includes(item));
export const replaceAll = (str, find, replace) => str.replaceAll(find, replace);
export const isAllUpperCase = (str) => str === str.toUpperCase() && /[A-Z]/.test(str);
export const isAllLowerCase = (str) => str === str.toLowerCase() && /[a-z]/.test(str);
export const toCharArray = (str) => Array.from(str);
export const reverseWords = (str) => str.split(' ').reverse().join(' ');
export const countWords = (str) => (str.match(/\b\w+\b/g) || []).length;
export const repeatWithSeparator = (str, count, sep) => Array(count).fill(str).join(sep);
export const trimStart = (str) => str.replace(/^\s+/, '');
export const trimEnd = (str) => str.replace(/\s+$/, '');
export const obfuscateEmail = (email) => {
if (!isEmail(email)) {
console.error(`provide valid email address${email}`);
return email;
}
;
const [user, domain] = email.split('@');
return `${user[0]}***@${domain}`;
};
export const base64Encode = (str) => Buffer.from(str, 'utf-8').toString('base64');
export const base64Decode = (str) => Buffer.from(str, 'base64').toString('utf-8');
export const camelToSnake = (str) => str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
export const snakeToCamel = (str) => trimChar(str.replace(/(_\w)/g, m => m[1].toUpperCase()), "_");
export const removeTrailingSlash = (str) => str.endsWith('/') ? str.slice(0, -1) : str;
export const removeLeadingSlash = (str) => str.startsWith('/') ? str.slice(1) : str;
export const splitByLength = (str, length) => str.match(new RegExp(`.{1,${length}}`, 'g')) || [];
export const truncateWords = (str, numWords) => str.split(' ').slice(0, numWords).join(' ') + '…';
export const isUUID = (str) => {
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(str);
};
export const generateUUID = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
export const removeDuplicateChars = (str) => Array.from(new Set(str)).join('');
export const percentEncode = (str) => {
return encodeURIComponent(str).replace(/[!'()*]/g, ch => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`);
};
export const percentDecode = (str) => decodeURIComponent(str);
export const getByteLength = (str) => new Blob([str]).size;
export const endsWithPunctuation = (str) => /[.!?]$/.test(str);
export const stringSimilarity = (a, b) => {
let matches = 0;
if (Math.max(a.length, b.length) === 0)
return 1; // Both empty, 100% similar
if (Math.min(a.length, b.length) === 0 && Math.max(a.length, b.length) > 0)
return 0; // One empty, one not, 0% similar
for (let i = 0; i < Math.min(a.length, b.length); i++) {
if (a[i] === b[i])
matches++;
}
return matches / Math.max(a.length, b.length);
};
export const censor = (str, words, mask = '*') => words.reduce((s, word) => s.replace(new RegExp(`\\b${word}\\b`, 'gi'), mask.repeat(word.length)), str);
export const safeJsonParse = (str) => {
try {
return JSON.parse(str);
}
catch {
return null;
}
};
export const mirrorString = (str) => str + str.split('').reverse().join('');
export const removeHtmlTags = (str) => str.replace(/<[^>]*>/g, '');
export const unescapeHtml = (str) => str.replace(/&/g, '&').replace(/</g, '<')
.replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '\'');
export const countCharacterOccurrences = (str, char) => {
return str.split(char).length + (!char ? +1 : -1);
};
export const extractInitials = (str) => str.split(' ').filter(word => word.length > 0).map(word => word[0].toUpperCase()).join('');
export const stripAnsiCodes = (str) => str.replace(/\x1B[[(?);]{0,2}(;?\d)*./g, '');
export const removeAllNumbers = (str) => str.replace(/\d+/g, '');
export const extractAllNumbers = (str) => str.match(/\d+/g) || [];
export const padCenter = (str, length, padChar = ' ') => {
if (str.length >= length)
return str;
const totalPadding = length - str.length;
const paddingStart = Math.floor(totalPadding / 2);
const paddingEnd = totalPadding - paddingStart;
return padChar.repeat(paddingStart) + str + padChar.repeat(paddingEnd);
};
export const hasEmoji = (str) => /[\u{1F600}-\u{1F6FF}]/u.test(str);
export const extractEmoji = (str) => Array.from(str.match(/[\u{1F600}-\u{1F6FF}|\u{1F300}-\u{1F5FF}|\u{1F900}-\u{1F9FF}|\u{2600}-\u{26FF}|\u{2700}-\u{27BF}]/gu) || []);
export const toCurrencyFormat = (numStr, currency = 'USD') => {
const num = Number(numStr);
if (isNaN(num))
return "NaN"; // Or throw error, or return original string
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(num);
};
export const stripSpaces = (str) => str.replace(/\s+/g, '');
export const extractDomain = (url) => {
try {
return new URL(url).hostname;
}
catch {
return null; // Return null for invalid URLs
}
};
export const extractTLD = (url) => {
try {
const hostname = new URL(url).hostname;
const parts = hostname.split('.');
if (parts.length > 1) {
return parts.pop() || null;
}
return null; // For hostnames like 'localhost' or single-label domains
}
catch {
return null; // Return null for invalid URLs
}
};
export const removeAlphanumeric = (str) => str.replace(/[a-z0-9]/gi, '');
export const getMiddleCharacter = (str) => {
if (str.length === 0)
return '';
return str[Math.floor((str.length - 1) / 2)]; // Corrected for even length to pick left of middle
};
export const insertAt = (str, index, value) => str.slice(0, index) + value + str.slice(index);
export const removeAt = (str, index, count = 1) => {
if (index < 0 || index >= str.length || count <= 0)
return str;
return str.slice(0, index) + str.slice(index + count);
};
export const reverseSentences = (str) => {
const sentences = str.match(/[^.!?]+[.!?]+/g) || [];
if (sentences.length === 0 && str.trim().length > 0)
return str; // Handle single phrase without punctuation
if (sentences.length === 0)
return '';
return sentences?.map((value) => value?.trim()).reverse().join(' ').trim();
};
export const capitalizeSentences = (str) => str.replace(/(^\s*\w|[.!?]\s*\w)/g, c => c.toUpperCase());
export const decapitalize = (str) => str.charAt(0).toLowerCase() + str.slice(1);
export const toUpperFirstChar = (str) => str.charAt(0).toUpperCase() + str.slice(1);
export const toLowerFirstChar = (str) => str.charAt(0).toLowerCase() + str.slice(1);
export const removeQuotes = (str) => str.replace(/^['"](.*)['"]$/, '$1').replace(/^'(.*)'$/, '$1').replace(/^"(.*)"$/, '$1');
export const surroundWithQuotes = (str, quoteType = '"') => `${quoteType}${str}${quoteType}`;
export const formatPhoneNumber = (num) => {
const cleaned = num.replace(/\D/g, '');
if (cleaned.length === 10) {
return cleaned.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}
if (cleaned.length === 11 && (cleaned.startsWith('1') || cleaned.startsWith('0'))) { // e.g. US with country code
return cleaned.slice(1).replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}
return num; // Return original if not a clear 10-digit or 11-digit US number
};
export const convertToBinary = (str) => {
if (!str)
return "";
return [...str].map(c => c.charCodeAt(0).toString(2).padStart(8, '0')).join(' '); // Pad to 8 bits
};
export const binaryToString = (bin) => {
if (!bin)
return "";
return bin.split(' ').map(b => String.fromCharCode(parseInt(b, 2))).join('');
};
export const convertToHex = (str) => {
if (!str)
return "";
return [...str].map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' '); // Pad to 2 hex chars
};
export const hexToString = (hex) => {
if (!hex)
return "";
return hex.split(' ').map(h => String.fromCharCode(parseInt(h, 16))).join('');
};
export const htmlEntityEncode = (str) => str.replace(/[\u00A0-\u9999<>\&"']/gim, i => `&#${i.charCodeAt(0)};`); // Added " and '
export const htmlEntityDecode = (str) => str.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec));
export const countLines = (str) => {
if (str === '')
return 0; // Or 1 for an empty string if it's considered one line
return str.split(/\r\n|\r|\n/).length;
};
export const getFirstLine = (str) => str.split(/\r?\n/)[0] || '';
export const getLastLine = (str) => str.split(/\r?\n/).pop() || '';
export const highlightSubstr = (str, substr, tagOpen = '**', tagClose = '**') => {
if (!substr)
return str;
// Escape special characters in substr for RegExp
const escapedSubstr = substr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return str?.replaceAll(new RegExp(escapedSubstr, 'g'), `${tagOpen}${substr}${tagClose}`);
};
export const replaceAt = (str, index, char) => {
if (index < 0 || index >= str.length)
return str;
return str.substring(0, index) + char + str.substring(index + 1);
};
export const stripLeadingZeros = (str) => {
if (str === '0')
return '0';
return str.replace(/^0+/, '');
};
export const removeDuplicatesWords = (str) => [...new Set(str.split(' '))].join(' ');
export const sortWords = (str) => str.split(' ').sort().join(' ');
export const uniqueWords = (str) => [...new Set(str.toLowerCase().match(/\b\w+\b/g) || [])];
export const toTitleCase = (str) => str.replace(/\b\w/g, l => l.toUpperCase());
export const slugToCamelCase = (str) => str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
export const camelCaseToSlug = (str) => str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
export const removeSpecialChars = (str) => str.replace(/[^\w\s]/gi, '');
export const countPunctuation = (str) => (str.match(/[.,!?;:]/g) || []).length;
export const countUppercase = (str) => (str.match(/[A-Z]/g) || []).length;
export const countLowercase = (str) => (str.match(/[a-z]/g) || []).length;
export const shuffleCharacters = (str) => [...str].sort(() => Math.random() - 0.5).join('');
export const containsUppercase = (str) => /[A-Z]/.test(str);
export const containsLowercase = (str) => /[a-z]/.test(str);
export const rotateString = (str, n) => {
if (str.length === 0)
return '';
const k = n % str.length;
return str.slice(k) + str.slice(0, k);
};
export const toggleCase = (str) => [...str].map(c => c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase()).join('');
export const reverseEachWord = (str) => str.split(' ').map(word => word.split('').reverse().join('')).join(' ');
export const splitToWords = (str) => str.trim().split(/\s+/).filter(Boolean);
export const countSentences = (str) => (str.match(/[\w|\)][.?!](\s|$)/g) || []).length;
export const extractSentences = (str) => str.match(/[^.!?]+[.!?]+\s*/g)?.map(s => s.trim()) || [];
export const generateAcronym = (str) => str.split(/\s+/).filter(Boolean).map(word => word[0].toUpperCase()).join('');
export const titleToSlug = (str) => str.toLowerCase().replace(/[^\w\s-]/g, '').trim().replace(/\s+/g, '-');
export const sanitizeFileName = (str) => {
return str.replace(/[^a-zA-Z0-9._-]/g, '_');
};
export const isIpAddress = (str) => {
if (!/^(\d{1,3}\.){3}\d{1,3}$/.test(str))
return false;
return str.split('.').every(part => parseInt(part, 10) <= 255);
};
export const isUrl = (str) => {
if (!str || typeof str !== 'string')
return false;
const normalized = str.startsWith('www.') || /^[a-zA-Z0-9.-]+\.[a-z]{2,}$/.test(str)
? `http://${str}`
: str;
try {
const url = new URL(normalized);
return ['http:', 'https:', 'ftp:'].includes(url.protocol);
}
catch {
return false;
}
};
export const getFileExtension = (str) => {
const lastDot = str.lastIndexOf('.');
if (lastDot === -1 || lastDot === 0 || lastDot === str.length - 1)
return ''; // No extension or hidden file
return str.substring(lastDot + 1);
};
export const removeFileExtension = (str) => {
const lastDot = str.lastIndexOf('.');
if (lastDot === -1 || lastDot === 0)
return str; // No extension or hidden file
return str.substring(0, lastDot);
};
export const isNumericString = (str) => /^\d+$/.test(str);
export const compactWhitespace = (str) => str.replace(/\s+/g, ' ').trim();
export const unescapeBackslashes = (str) => str.replace(/\\(.)/g, '$1');
export const stringToUnicode = (str) => [...str].map(c => '\\u' + c.charCodeAt(0).toString(16).padStart(4, '0')).join('');
export const unicodeToString = (unicodeStr) => unicodeStr.replace(/\\u([\dA-Fa-f]{4})/g, (_, g) => String.fromCharCode(parseInt(g, 16)));
export const removeVowels = (str) => str.replace(/[aeiou]/gi, '');
export const removeConsonants = (str) => str.replace(/[^aeiouAEIOU\s\d\W_]/gi, ''); // Keep vowels, spaces, digits, and symbols
export const alternateCase = (str) => [...str].map((c, i) => i % 2 ? c.toLowerCase() : c.toUpperCase()).join('');
export const randomStringBase36 = (length) => Array.from({ length }, () => Math.random().toString(36)[2]).join('');
export const obfuscatePhoneNumber = (num) => {
const match = num.match(/^(\d{5})(\d{4})(.*)$/);
return match ? `${match[1]}****${match[3]}` : num;
};
export const countWordsByLength = (str) => str.trim().split(/\s+/).filter(Boolean).reduce((acc, word) => {
const len = word.length;
if (len > 0) {
acc[len] = (acc[len] || 0) + 1;
}
return acc;
}, {});
export const stringToArrayBuffer = (str) => new TextEncoder().encode(str).buffer;
export const arrayBufferToString = (buf) => new TextDecoder().decode(buf);
export const isStrongPassword = (str) => /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\d\s:]).{8,}$/.test(str);
export const getLongestWord = (str) => {
const words = str.trim().split(/\s+/).filter(Boolean);
if (words.length === 0)
return '';
return words.reduce((a, b) => a.length >= b.length ? a : b, '');
};
export const getShortestWord = (str) => {
const words = str.trim().split(/\s+/).filter(Boolean);
if (words.length === 0)
return '';
return words.reduce((a, b) => (a.length <= b.length ? a : b));
};
export const getAllIndexesOf = (str, target, overlapping) => {
if (target.length === 0)
return Array.from({ length: str.length + 1 }, (_, i) => i);
const indices = [];
let i = -1;
while ((i = str.indexOf(target, i)) !== -1) {
indices.push(i);
if (!overlapping) {
i = i + target?.length;
}
else {
i++;
}
}
return indices;
};