Skip to main content

Як порахувати довжину рядка без вбудованих методів чи властивостей (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-послідовності й комбіновані діакритики). Для точного підрахунку потрібен алгоритм сегментації графем.

Рішення

  1. Підрахунок кодпоінтів без методів і length (переважно на співбесіді):
function codePointLength(str) { let n = 0; for (const _ of str) n++; return n; }
  1. Підрахунок код-юнітів без методів і length (суворе повторення логіки String.length):
function codeUnitLength(str) { let i = 0; while (true) { if (str[i] === undefined) return i; i++; } }
  1. Якщо потрібна довжина графемних кластерів: без зовнішніх бібліотек найпростіше скористатися стандартним 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

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.