В прошлый раз мы изучили, как разбить проблему на более мелкие задачи, чтобы найти решение: теперь давайте проведем рефакторинг нашего решения и рассмотрим стратегии, которые могут помочь нам понять как наш код, так и язык программирования, который мы используем.
В прошлый раз проблема заключалась в том, чтобы взять строку и манипулировать ею так, чтобы каждый символ в строке был написан с заглавной буквы и повторялся в соответствии с их позицией индекса.
function repeat(string)repeat("abcd") -> "A-Bb-Ccc-Dddd" repeat("RqaEzty") -> "R-Qq-Aaa-Eeee-Zzzzz-Tttttt-Yyyyyyy" repeat("cwAt") -> "C-Ww-Aaa-Tttt"
Я придумал следующее решение:
const string = "abcde"; dupCharByIndex = (s) =>{ const chars = s.split(''); return chars.map((char,index)=>{ return char.toLowerCase().repeat(index + 1); }); }; function capAndHyphenArray(s){ const chars = dupCharByIndex(s); return chars.map(char => { if (char.length !== 1){ const charArray = char.split(''); const cap = charArray[0].toUpperCase(); const hyphen = "-"; charArray.shift(); charArray.unshift(hyphen + cap); return charArray.join(''); } else { return char.toUpperCase(); }; }).join(''); }; capAndHyphenArray(string) //A-Bb-Ccc-Dddd-Eeeee
В этом решении много чего происходит, поэтому давайте попробуем найти способы сделать решение более эффективным и упростить код, чтобы его было легче читать. Во-первых, давайте начнем с рассмотрения большой буквы О. При этом мы должны искать, где с нашим решением приходится работать больше всего. Должно ли решение перебирать данные, и если да, то есть ли внутри вложенные итерации? Для этого решения у нас есть пара итераций, но нет вложенных итераций, делающих решение O (n)
dupCharByIndex(string) chars.map((char,index) capAndHyphenArray(s) const chars = dupCharByIndex(s); chars.map(char => { if (char.length !== 1){ const charArray = char.split('');
Наше решение имеет вложенную итерацию в функции capAndHyphenArray. Мы отображаем символы, а затем для каждого символа, длина которого не равна 1, мы разделяем символы. Это делает нашу функцию очень дорогой с точки зрения затрат памяти. Таким образом структурируя функции, мы заставляем компьютер выполнять массу работы. Возьмите эту строку, разделите ее, повторите каждый символ по его индексу, затем возьмите эту новую строку, и если строка длиннее одной, разделите ее также на массив. Из-за вложенного итератора у нас есть решение O (n²). Как мы можем сделать это более эффективным?
При поиске способа сделать решение более эффективным следует учитывать противоположное тому, о чем мы думали, когда изначально пытались решить проблему. Какие задачи мы можем объединить или упростить? Начнем с dupCharByIndex (строка).
dupCharByIndex = (s) =>{ const chars = s.split(''); return chars.map((char,index)=>{ return char.toLowerCase().repeat(index + 1); }); };
Что здесь можно сделать, чтобы упростить это? Поговорим об операторе спреда.
моя переменная chars - хорошая ссылка, но мы можем преобразовать эту переменную в строку .map () с помощью оператора распространения
const chars = s.split(''); is equivalent to: [...s];
поэтому вместо того, чтобы моя функция задавала переменную, а затем вызывала ее, мы можем упростить ее как таковую:
dupCharByIndex = (s) =>{ return [...s].map((char,index)=>{ /// etc...
Это может показаться не таким уж большим делом, но исключение строк кода окупится, когда вы работаете с файлами, состоящими из сотен, если не тысяч, если не десятков тысяч строк.
Пока мы говорим об упрощении, давайте рассмотрим роль dupCharByIndex. Мы знаем, что используем его в нашей функции capAndHyphenArray, поэтому вместо повторного вызова, сделав его переменной, давайте просто сделаем это в функции capAndHyphenArray. Кроме того, давайте изменим имя функции, поскольку она будет выполнять другую работу.
Старый:
function capAndHyphenArray(s){ const chars = dupCharByIndex(s); return chars.map(char => { if (char.length !== 1){ const charArray = char.split(''); // etc...
Новый:
function repeatCharByIndex(s){ return [...s].map((char,index)=>{ return char.toLowerCase().repeat(index + 1)
Давайте сделаем еще один шаг, вот остальная часть логики моей старой функции capAndHyphen:
return chars.map(char => { if (char.length !== 1){ const charArray = char.split(''); const cap = charArray[0].toUpperCase(); const hyphen = "-"; charArray.shift(); charArray.unshift(hyphen + cap); return charArray.join(''); } else { return char.toUpperCase(); }; }).join('');
Давайте сделаем это более эффективным, взяв задачу повторения символов, а также объединим ее с нашей задачей использования заглавных букв и нашей задачей добавления дефиса:
function
repeatCharByIndex(s) { return [...s].map((element, index) => { return element.toUpperCase() + element.toLowerCase().repeat(index); }).join('-'); }
Здесь мы возвращаем элемент в s (char), пишем его заглавными буквами, а затем добавляем дополнительный элемент в нижнем регистре и повторяем его на основе его индекса. Теперь мы устранили вложенную итерацию и превратили эту функцию из O (n²) в O (n):
const string = "abcde"; dupCharByIndex = (s) =>{ const chars = s.split(''); return chars.map((char,index)=>{ return char.toLowerCase().repeat(index + 1); }); }; function capAndHyphenArray(s){ const chars = dupCharByIndex(s); return chars.map(char => { if (char.length !== 1){ const charArray = char.split(''); const cap = charArray[0].toUpperCase(); const hyphen = "-"; charArray.shift(); charArray.unshift(hyphen + cap); return charArray.join(''); } else { return char.toUpperCase(); }; }).join(''); }; capAndHyphenArray(string) //A-Bb-Ccc-Dddd-Eeeee
в это:
const string = "abcde";function
repeatCharByIndex(s) { return [...s].map((element, index) => { return element.toUpperCase() + element.toLowerCase().repeat(index); }).join('-'); }
repeatCharByIndex(string) //
A-Bb-Ccc-Dddd-Eeeee
На этом все, но по мере того, как я продолжаю работать над более сложными проблемами, обязательно напишу еще один пост, посвященный более продвинутым концепциям и решениям.