문제에서 주어진 조건
1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.
2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.
3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.
4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.
6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.
7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.
정규표현식을 막 입문해서인지, 처음 풀 때부터 문제에 적용해보고 싶었지만 감을 잘 못잡았다.
그래서 우선 정규표현식 없이 코드를 작성해보았고, 일단은 pass✅
나의 코드
function solution(new_id) {
const step1 = new_id.split('').map((v)=> v=== v.toUpperCase() ? v.toLowerCase() : v);
const step2 = step1.filter((v)=> v.toLowerCase() !== v.toUpperCase() || !isNaN(v) || v === "-" || v === "_" || v === "." );
const step3 = step2.map((v,i,a)=> v === "." & a[i+1] === "." ? "" : v).join('');
const step4_1 = step3[0] === "." ? step3.substring(1) : step3;
const step4_2 = step3[step4_1.length - 1] === "." ? step4_1.substring(0,step3.length - 1) : step4_1;
const step5 = step4_2.length === 0 ? "a" : step4_2;
const step6_1 = step5.substring(0, 15);
const step6_2 = step6_1[step6_1.length - 1] === "." ? step6_1.substring(0,step6_1.length - 1) : step6_1;
return step7 = step6_2.padEnd(3,step6_2[step6_2.length - 1]);
}
문제에서 주어진 7단계 처리 과정을 고려하여 변수명을 지었다.
map 사용. 대문자를 소문자로 변환하기 위해 특정 글자가 해당 글자의 대문자와 같을 경우 소문자로 변환.
(근데 아래 보면 알겠지만 굳이 map 쓸 필요 없이 통째로 소문자로 바꾸면 되는 것이었다ㅎㅎ,, 삐슝빠슝)
filter 사용. 영문인지 확인하기 위해 v.toLowerCase() !== v.toUpperCase() 사용.
소문자와 대문자가 다른 것은 영문 밖에 없으니까! 숫자인지 확인하기 위해서는 !isNaN(v) 사용.
map 사용.
배열에서 나도 .이고 내 다음 글자도 .이면 나를 ""으로 변환. 이렇게 해서 .이 연달아 나오는 일은 없도록 만들었다.
첫번째 글자가 .이면 2번째 글자부터 가져오기 위해 substring(1) 사용.
마지막 글자가 .이면 substring(0,step3.length - 1)을 사용하여 마지막 글자만 빼고 가져옴.
글자가 모두 사라져서 글자 길이가 0이 되어버린다면, "a"가 되도록 함.
현재 글자 길이에 관계 없이 substring(0,15)를 사용하여 앞에서부터 15글자만 가져옴.
substring 이용. 마지막 글자가 .이라면 뒤의 한글자를 빼고 가져옴. step4_2와 같음.
padEnd 사용하여, 최소 3자리는 나타나게 함. 이때 현재 3글자 미만이라면 끝 부분을 마지막 글자로 채움.
그으런데 이 문제도 역시 정규표현식으로 푸는 사람들이 많았다.
그것이 바로 블로그에 이 문제를 가져온 이유!
나는 정규표현식린이(?)라 적용을 못했지만 이번 기회에 좀 더 봐봐야지.
정규표현식을 이용한 코드
function solution(new_id) {
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, '') // 2
.replace(/\.+/g, '.') // 3
.replace(/^\.|\.$/g, '') // 4
.replace(/^$/, 'a') // 5
.slice(0, 15).replace(/\.$/, ''); // 6
const len = answer.length;
return len > 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len);
}
앗.. 첫 줄부터 허탈..
그냥 new_id 전체에 toLowerCase()쓰면 싹 다 소문자로 바뀌는 거군..
나처럼 굳이 map을 걸어서 일일히 한 글자 씩 바꿔줄 필요가 없었닼ㅋㅋㅋㅋㅋ
이제 정규표현식을 하나하나 파보자.
2단계
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, '') // 2
/[^\w-_.]/g
패턴에서 -이거나, _이거나, .이 아닌 모든 것들은 공백으로 변환하라는 건 알겠는데,
\w는 무엇..?
\w는 모든 문자열을 의미하는 것이었다. (영문과 숫자도 포함.)
숫자나 영문이 아니라면(^) ''로 바꿔줘라!
주의할 것은 소문자w를 써야한다는 것이다.
대문자로 \W라고 쓰면 반대 의미가 된다. 문자열을 제외한 모든 것들을 선택.
즉 \W와 ^\w는 같은 것이다.
3단계
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, '') // 2
.replace(/\.+/g, '.') // 3
\.+가 무엇..?
+는 one or more의 의미이다.
.이 1개 이상 나오면 .으로 바꾸라는 의미!
오케 근데 console을 찍어봤더니 \를 지우면 new_id 전체 다 .으로 replace 되어 나온다. why?
.은 어떤 글자(줄바꿈 문자 제외)를 나타내는 기호이다.
그래서 \ 없이 .만 찍으면 줄바꿈 문자를 제외한 모든 글자(줄바꿈 문자 제외)를 의미한다.
뿌에엥 나는 문자 그대로의 .을 찾고 싶다규~!
그럴 때는 \을 앞에 표기해서 .이 내가 찾고자 하는 문자라는 것을 알려줘야 한다.
\ : 특수문자열 자체를 검색
4단계
const answer = new_id
.toLowerCase()
.replace(/[^\w-_.]/g, '')
.replace(/\.+/g, '.')
.replace(/^\.|\.$/g, '')
^\.|\.$가 무엇..?
^\.에서 ^는 문장의 시작을 의미한다.
즉, 문장에서 시작하는 .이라는 기호를 의미함. (근데 부정의 ^랑 어떻게 다른 걸까 [] 외부에서 쓰이는지 내부에서 쓰이는지 차이인가?)
\.$에서 $는 문장의 끝을 의미한다.
문장 끝의 .이라는 기호를 의미함.
^\. 와 \.$ 사이에 |를 입력하는데
|는 또는 이라는 뜻이다.
5단계
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, '') // 2
.replace(/\.+/g, '.') // 3
.replace(/^\.|\.$/g, '') // 4
.replace(/^$/, 'a') // 5
^$가 무엇...?
문자가 ""으로 구성되었다는 것이다. why?
예를 들어 ^[A-Z]$
라고 한다면 알파벳 대문자로만 구성된 문자를 찾아준다.
그런데 ^$
라고 한다면 모든 구성요소가 ""이므로 그냥 ""임을 의미.
6단계
const answer = new_id
.toLowerCase() // 1
.replace(/[^\w-_.]/g, '') // 2
.replace(/\.+/g, '.') // 3
.replace(/^\.|\.$/g, '') // 4
.replace(/^$/, 'a') // 5
.slice(0, 15).replace(/\.$/, ''); // 6
이제 \.$은 뭔지 알겠다!
문자열이 .으로 끝난다면! 이라는 뜻~~~
마지막 글자가 .이라면 공백으로 바꿔라!
오늘의 정규표현식 공부 끝!
배운 거 써먹고 또 써먹고
새로운 것 보이면 또 기록해서 확장하는 식으로 조금씩 익혀나가자.
화이팅!