Итак, я уверен, что другие люди сталкивались с этой проблемой раньше, будь то в JavaScript или каком-то другом языке.

Моя последняя задача по программированию заключалась, по сути, в создании функции, которая возвращала бы строку в заглавном регистре (т. е. «Каждое слово пишется с заглавной буквы»), если бы ей давали строку с непоследовательным использованием заглавных букв («Каждое слово пишется с большой буквы»).

Быстрый поиск в Google показал, что метода .toTitleCase() не существует (возможно, из-за некоторых проблем, о которых я расскажу ниже). И хотя другие люди решили эту проблему и опубликовали несколько решений, многие из них использовали выражения, которые мне неизвестны. Я хотел на самом деле понять, что я делаю, поэтому я решил найти собственное решение.

Первое, что я понял, это то, что самый простой способ определить первую букву каждого слова — это найти пробелы в предоставленной строке, а затем сделать следующую букву заглавной. Вот мой первый удар по функции:

function capitalizer(title){
let characterNumber = 0;
title = title.replace(title[0], title[0].toUpperCase());
while (characterNumber ‹ title. length){
if (title[characterNumber] === “ ”){
characterNumber++;
title = title.replace(title[characterNumber], title[characterNumber].toUpperCase()) ;
} else {
номер_символа++;
}
}
вернуть заголовок;
}

Самое интересное, это сработало! Я вставил тестовую строку: «лучший из всех возможных миров», и она вернула «лучший из всех возможных миров». Итак, я подумал: это было намного проще, чем ожидалось!

Неправильный.

При передаче заголовков из примера класса («Что означает это ключевое слово?» или «В чем разница между stopPropagation и preventDefault?») я получил странные и, казалось бы, случайные результаты: «Что означает это ключевое слово?» или «В ЧЕМ РАЗНИЦА МЕЖДУ stoPPropagation и preventDefault?»… Подождите, что??

Я смотрел и смотрел на свой код. Я попытался массировать функцию, скопировав строку и оставив оригинал нетронутым. Я пытался использовать разные методы для доступа к отдельным письмам. Не повезло.

В конце концов я понял, что JavaScript берет title.replace(…) и оценивает предоставленные мной аргументы (например, title[characterNumber]) в строки. Таким образом, он обработал (например) title.replace(‘t’, ‘T’) и, что особенно важно, потерял все ссылки на позицию индекса символа в строке. Таким образом, он заменит первую найденную строчную букву «t», а не ту, которая находится в указанном месте.

Как только я понял это, я перешел к попытке разбить строку на отдельные слова и соединить каждое из них в новую строку после того, как первая буква была заглавной. Я не совсем уверен, почему это была такая борьба, но, так или иначе, у меня не получилось с этим подходом.

Наконец, мне пришло в голову, что нет необходимости разбивать все на слова, и что я могу просто объединять по одной букве в новую строку, делая заглавной любую букву, которая идет после пробела (или в начале общего слова). нить). Это решение оказалось лучшим, и код, который я использовал для его достижения, находится здесь:

function capitalizer(title){
const iterator = title[Symbol.iterator]();
let currentLetter = iterator.next();
let firstLetter = true;
let newTitle = "";
while (!currentLetter.done) {
if (firstLetter === true) {
newTitle = newTitle.concat("" currentLetter.value.toUpperCase());
firstLetter = false;
} else if (currentLetter.value === ““) {
firstLetter = true;
newTitle = newTitle.concat(“”, currentLetter.value );
} else {
newTitle = newTitle.concat(“”, currentLetter.value);
}
currentLetter = iterator.next();
}< br /> вернуть новый заголовок;
}

Оно работает! Ну наконец то! Я не уверен, что это самый чистый код для этой цели, но я горжусь тем, что разработал его.

Теперь пара предостережений. В этом упражнении я проигнорировал тот факт, что в правильном регистре заглавий есть несколько слов («а», «то», «из», «и» и т. д.), которые не получить капитализацию. Я могу придумать по крайней мере пару способов запрограммировать эти исключения, если бы я писал более надежную функцию Title Case.

Более сложной будет проблема с заглавными буквами, которые не стоят в начале слов, например, если бы вам дали строку: 'everYone loves cAts'. Очевидно, вы могли бы начать функцию преобразовав строку в нижний регистр с помощью title = title.toLowerCase(). Это решило бы указанную выше проблему, но как насчет строки типа «родился в США»? Вы хотели бы сохранить заглавные буквы «США», и предлагаемое решение в конечном итоге вернет «США», что неверно. Еще хуже были бы имена собственные, такие как «O’Reilly» или «PowerPoint», в которых нет подсказок о том, что их начальная буква не единственная, которую нужно писать с большой буквы. У меня нет решения для этого; Я предполагаю, что это настоящая причина, по которой не существует метода .toTitleCase().

Впрочем, какое приключение! Это была первая настоящая головная боль, с которой я столкнулся в своем программировании. Я рад, что разобрался с проблемой и смог поделиться своим опытом здесь.

РЕДАКТИРОВАТЬ: Вскоре после публикации этого мой мозг все еще пережевывал проблему, и у меня внезапно возникло вдохновение: что, если вместо замены только отдельной буквы у меня был бы метод .replace(), заменяющий пробел? Добавление пробела в строку замены гарантирует, что только буквы, следующие за пробелом, будут заглавными (плюс дополнительная строка кода для заглавной самой первой буквы в строке). Нравится:

function capitalizer(title){
let characterNumber = 0;
title = title.replace(title[0], title[0].toUpperCase());
while (characterNumber ‹ title. length){
if (title[characterNumber] === " "){
title = title.replace(" "+ title[characterNumber + 1], " " +
title[characterNumber + 1].toUpperCase());
}
characterNumber++;
}
вернуть заголовок;
}

Привет! Оно работает! И короче. Теперь мой мозг начинает думать: а не должен ли быть встроенный метод для перебора строки и поиска всех пробелов? Время для небольшого чтения документации, и…

function capitalizer(title){
title = title.padStart(title.length + 1);
for (const match of title.matchAll(/\b\w/g)){
title = title.replace(' ' + title[match.index], ' ' +
title[match.index].toUpperCase());
}
return title.trim() ;
}

Тарабарщина в функции .matchAll() — это, безусловно, то, что я пока плохо понимаю, но в основном она найдет все буквы, которым предшествует пробел. Я до сих пор не могу понять, как заставить строку заменить символ по заданному индексу; добавление пробела в обе половины метода .replace() — это обходной путь, которого будет недостаточно во всех случаях. Чтобы сократить этот код и заставить оператор for… работать, мне пришлось поставить пробел в начале строки заголовка, запустить оператор, а затем снова удалить этот пробел. Небрежно, но эффективно.

В любом случае, если я найду способ принудительно заменить позицию в строке, я опубликую это. Это был бы самый чистый вариант.

Вперед!