[프로그래머스 1레벨] 신규 아이디 추천

이민선(Jasmine)·2023년 1월 13일
1
post-thumbnail

문제에서 주어진 조건

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단계 처리 과정을 고려하여 변수명을 지었다.

step1

map 사용. 대문자를 소문자로 변환하기 위해 특정 글자가 해당 글자의 대문자와 같을 경우 소문자로 변환.
(근데 아래 보면 알겠지만 굳이 map 쓸 필요 없이 통째로 소문자로 바꾸면 되는 것이었다ㅎㅎ,, 삐슝빠슝)

step2

filter 사용. 영문인지 확인하기 위해 v.toLowerCase() !== v.toUpperCase() 사용.
소문자와 대문자가 다른 것은 영문 밖에 없으니까! 숫자인지 확인하기 위해서는 !isNaN(v) 사용.

step3

map 사용.
배열에서 나도 .이고 내 다음 글자도 .이면 나를 ""으로 변환. 이렇게 해서 .이 연달아 나오는 일은 없도록 만들었다.

step4_1

첫번째 글자가 .이면 2번째 글자부터 가져오기 위해 substring(1) 사용.

step4_2

마지막 글자가 .이면 substring(0,step3.length - 1)을 사용하여 마지막 글자만 빼고 가져옴.

step5

글자가 모두 사라져서 글자 길이가 0이 되어버린다면, "a"가 되도록 함.

step6_1

현재 글자 길이에 관계 없이 substring(0,15)를 사용하여 앞에서부터 15글자만 가져옴.

step6_2

substring 이용. 마지막 글자가 .이라면 뒤의 한글자를 빼고 가져옴. step4_2와 같음.

step7

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

이제 \.$은 뭔지 알겠다!
문자열이 .으로 끝난다면! 이라는 뜻~~~
마지막 글자가 .이라면 공백으로 바꿔라!

오늘의 정규표현식 공부 끝!
배운 거 써먹고 또 써먹고
새로운 것 보이면 또 기록해서 확장하는 식으로 조금씩 익혀나가자.
화이팅!

profile
기록에 진심인 개발자 🌿

0개의 댓글