Як порахувати довжину рядка без вбудованих методів чи властивостей (length)?
Коротка відповідь
Порахувати довжину рядка можна простим проходом по символах з інкрементом лічильника, не використовуючи length і методи рядків. Нижче два базові варіанти: через for..of (підрахунок Unicode-кодпоінтів) і через індексацію (підрахунок UTF-16 код-юнітів).
// Варіант A: за кодпоінтами (враховує surrogate pairs)
function strLengthByIteration(str) {
let count = 0;
for (const _ of str) count++;
return count;
}
// Варіант B: за код-юнітами (як String#length)
function strLengthByIndexing(str) {
let i = 0;
while (true) {
if (str[i] === undefined) return i;
i++;
}
}Детальне пояснення
Що саме ви вважаєте «довжиною»
- Код-юніти (UTF-16): відповідає поведінці String.length та індексу str[i]. Емодзі поза BMP займають 2 код-юніти.
- Кодпоінти (Unicode code points): for..of коректно об'єднує surrogate pairs, рахуючи такі символи як один.
- Графемні кластери (те, що користувач бачить як один символ): можуть складатися з кількох кодпоінтів (наприклад, ZWJ-послідовності й комбіновані діакритики). Для точного підрахунку потрібен алгоритм сегментації графем.
Рішення
- Підрахунок кодпоінтів без методів і length (переважно на співбесіді):
function codePointLength(str) {
let n = 0;
for (const _ of str) n++;
return n;
}- Підрахунок код-юнітів без методів і length (суворе повторення логіки String.length):
function codeUnitLength(str) {
let i = 0;
while (true) {
if (str[i] === undefined) return i;
i++;
}
}- Якщо потрібна довжина графемних кластерів: без зовнішніх бібліотек найпростіше скористатися стандартним API сегментації (якщо його використання допустиме):
function graphemeLength(str) {
if (typeof Intl !== 'undefined' && Intl.Segmenter) {
const seg = new Intl.Segmenter('uk', { granularity: 'grapheme' });
let count = 0;
for (const _ of seg.segment(str)) count++;
return count;
}
// Фолбек: рахуємо кодпоінти
let n = 0;
for (const _ of str) n++;
return n;
}Перевірка й приклади
const samples = [
"Hello",
"Привіт",
"\u{1F600}", // один кодпоінт, дві код-юніти
"\u{1F468}\u{1F469}\u{1F467}\u{1F466}", // сім'я: кілька кодпоінтів, один видимий символ
"é", // e + комбінований наголос, два кодпоінти, один видимий символ
"\u0000abc" // містить нульовий символ
];
for (const s of samples) {
console.log('s =', JSON.stringify(s));
console.log('codePointLength:', codePointLength(s));
console.log('codeUnitLength :', codeUnitLength(s));
console.log('graphemeLength :', graphemeLength(s));
console.log('---');
}Крайні випадки й нюанси
- Порожній рядок: обидва базові алгоритми повернуть 0.
- Нульовий символ всередині рядка: індексація безпечна, оскільки перевіряється строго на undefined, а не за truthy/falsy.
- Емодзі й символи поза BMP: for..of враховує surrogate pairs і дає коректний лічильник кодпоінтів.
- Комбіновані діакритики й ZWJ-послідовності: один видимий символ може складатися з кількох кодпоінтів; для цього використовуйте сегментацію графем.
- Продуктивність: усі варіанти мають O(n) за часом і O(1) за пам'яттю.
Складність
Час - O(n), пам'ять - O(1), де n - довжина вхідного рядка в обраній одиниці (код-юніти/кодпоінти/графеми).
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.