코드짜는 노인네

[프로그래머스] 신규 아이디 추천 - 문제해결과정 본문

코딩 테스트

[프로그래머스] 신규 아이디 추천 - 문제해결과정

ikohong 2022. 9. 9. 18:19
728x90
반응형

[프로그래머스] 신규 아이디 추천 - 문제해결과정


문제 : 사용자가 입력한 새로운 ID 생성을 조건에 따른 신규 아이디 추천 알고리즘 구현 
조건 : 
- 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이 될 때까지 반복해서 끝에 붙입니다.
EX > "...!@BaT#*..y.abcdefghijklm"
answer > "bat.y.abcdefghi"

나의 문제 해결 과정 (에러과정)


일단 단계별로 차근차근 진행을 해보기로 했습니다. 1단계의 경우 파이썬의 함수를 사용하면 되기에 금방 해결이 되었습니다.

# 1단계 new_id의 모든 대문자를 대응되는 소문자로 치환합니다.

lower_new_id = new_id.lower()

다음 '2단계'에서 고생을 많이 했고, 시간이 가장 많이 걸렸습니다. 맨 처음 생각을 해낸건 '알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)'의 문자만 리스트에 정리를 해야겠는 생각이 들었습니다.

check_new_id = ["-","_","."]

for i in range(48,58):
    check_new_id.append(chr(i))
    
for i in range(97,123):
    check_new_id.append(chr(i))

그냥 리스트로 넣어 만들려고 하니, 뭔가 없어(?)보여서 '아스키코드'를 이용해서 숫자와 소문자를 'check_new_id'리스트에 추가 하였습니다. 그리고, 문자열을 조회해서 해당 문자열을 조회해서 'check_new_id'에 포함되어있지 않는 문자를 제거할려고 했지만, 방법이 마땅히 떠오르지가 않았습니다. 그래서 문자열을 리스트로 변환한 다음, 제거를 시도하였습니다.

lower_new_id_list = list(lower_new_id)

for i in range(0,len(lower_new_id_list)):
    if lower_new_id_list[i] not in check_new_id:
        lower_new_id_list.remove(lower_new_id_list[i])

허나, for문을 통해 루프를 돌리면서, 포함되지 않은 문자가 발견되었을때, 'remove()'함수를 이용하여, 리스트를 제거하면, 전체 길이가 줄어들면서 범위에서 벗어난다는 생각을 하지 못하여, 에러가 발생하였습니다. 그래서 생각을 좀 틀어서 '제거해야될 특수 문자들의 모음을 만들자'라는 생각을 하게 되었습니다.

# 2단계 new_id에서 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다.

import string

new_symbols = string.punctuation.replace('-','').replace('_','').replace('.','')

for i in new_symbols:
    if i in lower_new_id:
        lower_new_id = lower_new_id.replace(i,'')

구글링을 하다보니 파이썬에서는 'punctuation'라는 특수문자를 모아둔 코드가 있다는걸 알게되어, 특수문자들중 '빼기(-)','밑줄(_)','마침표(.)'를 제외한 특수문자들을 'new_symbols'변수에 넣은 다음, for문을 이용하여 'new_symbols'특수문자가 문자열 'lower_new_id'에 있는지 확인한 다음, 해당 특수문자가 있을경우, 'replace()'함수를 이용하여, 제거를 했습니다. [ 단, punctuation을 사용하기 위하서는 string을 import 시켜야 한다. ]

# 3단계 new_id에서 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다.

# 문자열 'lower_new_id'를 리스트로 변환
lower_new_id_list = list(lower_new_id)
pop_dot = []

# 마침표가 2번 이상 연속되는지 체크후, 해당 위치를 pop_dot 배열에 추가
for i in range(0,len(lower_new_id_list)):
    if lower_new_id_list[i] == '.':
        try:
            if lower_new_id_list[i] == lower_new_id_list[i+1]:
                pop_dot.append(i)
        except:
            pass

마침표(.)가 2번 이상 연속으로 나오는걸 없애기위해 일단 문자열인 상태로 마침표(.)의 index를 찾아볼려했습니다. 허나 문자열을 index로 위치를 조회할려고 하니, 맨 앞에 있는 문자열의 index 번호만 조회가 되어서, 문자열을 리스트로 변환하고 난 다음, 해당 리스트에서 마침표(.)가 있을 경우, 다음 원소에 마침표(.)가 있는지 확인 후 있다면, 'pop_dot'리스트에 해당 원소의 'index'번호를 추가했습니다.

그 다음, 'lower_new_id_list'에서 해당 원소를 'pop()'함수를 이용해서 제거할려고 시도 했습니다.

for i in pop_dot:
    lower_new_id_list.pop(i)

허나, 결과를 출력해보니, 이상한 값이 나오거나, 'list Index out of range'경고창이 나와서 무엇이 문제인가 생각을 했는데, 리스트에서 2번 이상 연속되는 마침표(.)를 앞에서부터 제거를 하게 되면, 뒤에 원소순서가 바뀌어서 틀어지게 된다는걸 뒤늦게 눈치챘습니다.

# pop이 되고 나면 index의 위치가 꼬임 >> 리스트를 역순서로 뒤에부터 제거할 수 있도록 수정
pop_dot = list(reversed(pop_dot))
for i in pop_dot:
    lower_new_id_list.pop(i)

# list를 문자열로 재생성
lower_new_id = ''.join(s for s in lower_new_id_list)
# 문자열로 변경하고 나니, 문자마다 띄워져있어서 띄워진 공간을 제거
lower_new_id = lower_new_id.replace(' ','')

그래서 'pop_dot'리스트를 'reversed()'함수를 이용해 역순으로 만든다음, 'lower_new_id_list'에서 마침표(.)를 제거 했습니다. 그리고 난 다음, 해당 리스트를 문자열로 다시 바꾸었는데, 문자열로 바꾸니깐 문자마다 띄어쓰기가 되어있어서 띄워쓰기 되어있는 부분을 제거를 했습니다.

# 4단계 new_id에서 마침표(.)가 처음이나 끝에 위치한다면 제거합니다.
lower_new_id = lower_new_id.strip('.')

4단계는 간단했습니다. 'strip'함수를 이용해서 양쪽에 마침표(.)가 있는지 확인후, 제거를 할 수 있었습니다.

# 5단계 new_id가 빈 문자열이라면, new_id에 "a"를 대입합니다.

if lower_new_id == '' or lower_new_id == None:
    lower_new_id = "a"

5단계에서는 사용자가 id를 입력했을때, 아이디가 없을경우, 'a'를 대입할 수 있도록 하는 코드였는데, 간단하게 'if'문을 사용하여서, 비어있거나, 값이 없을 경우, 'a'를 넣을수 있도록 설계하였습니다.

# 6단계 new_id의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
#      만약 제거 후 마침표(.)가 new_id의 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다.

if len(lower_new_id) >= 16:
    lower_new_id = lower_new_id[:15]
    
lower_new_id = lower_new_id.rstrip('.')

6단계는 해당 문자열의 길이가 16자 이상인 경우, 첫 15개의 문자를 제외한 나머지 문자들을 제거하라는거였는데, 글자의 길이를 알아보기 위해 'len()'함수를 사용했으며, 'if'문을 이용하여, 16자 이상일 경우, 문자열 슬라이싱(lower_new_id[:15] : 0~14까지의 문자열로 슬라이싱)를 이용하여, 자른 다음, 아까랑 다르게 마침표(.)가 끝에 위치할 경우 제거를 한다고 하여, 'rstrip()'함수를 사용하여, 제거하도록 했습니다.

# 7단계 new_id의 길이가 2자 이하라면, new_id의 마지막 문자를 new_id의 길이가 3이 될 때까지 반복해서 끝에 붙입니다.

if len(lower_new_id) <= 2:
    while True:
        lower_new_id += lower_new_id[(len(lower_new_id)-1)]
        if len(lower_new_id) == 3:
            break

마지막으로 문자열의 길아가 '2'이하일 경우의 조건이기에 'if'문의 조건문을 작성한 다음, 길이가 3일 될 때까지이기 때문에 'while'문을 사용하여, 길이가 3이 될 때까지, 마지막 문자열을 추가하고, 길이가 3이 되었을 경우, 반복문(while)에서 벗어나도록 break를 작성해 코드를 마무리 하였습니다.

반응형

최종 코드 결과물


import string

def solution(new_id):

    lower_new_id = new_id.lower()

    new_symbols = string.punctuation.replace('-','').replace('_','').replace('.','')

    for i in new_symbols:
        if i in lower_new_id:
            lower_new_id = lower_new_id.replace(i,'')

    lower_new_id_list = list(lower_new_id)
    pop_dot = []

    for i in range(0,len(lower_new_id_list)):
        if lower_new_id_list[i] == '.':
            try:
                if lower_new_id_list[i] == lower_new_id_list[i+1]:
                    pop_dot.append(i)
            except:
                pass

    pop_dot = list(reversed(pop_dot))
    
    for i in pop_dot:
        lower_new_id_list.pop(i)

    #list를 문자열로 재생성
    lower_new_id = ''.join(s for s in lower_new_id_list)
    lower_new_id = lower_new_id.replace(' ','')

    lower_new_id = lower_new_id.strip('.')

    if lower_new_id == '' or lower_new_id == None:
        lower_new_id = "a"

    if len(lower_new_id) >= 16:
        lower_new_id = lower_new_id[:15]

    lower_new_id = lower_new_id.rstrip('.')

    if len(lower_new_id) <= 2:
        while True:
            lower_new_id += lower_new_id[(len(lower_new_id)-1)]
            if len(lower_new_id) == 3:
                break

    answer = lower_new_id

    return answer

 

프로그래머스에서 테스트를 통과했지만, 다른분들의 풀이를 보니깐 '이야... 난 뭐했나...'라는 생각이 절로 들었습니다. 정규식을 이용하게되면, 코드가 확 줄어드는걸 보고, 아직까지 더 공부를 해야겠다는 생각이 탁 들더라구요;;; 아직은 코드가 정리도 안되고, 미숙하게 짜고 있지만, 언젠가는 깔끔한 코드를 짜는 그날까지 열심히 공부를 해야겠습니다.

728x90
반응형
Comments