[프로그래머스] Lv.0 코드 처리하기
🔖 문제 설명
문자열 code
가 주어집니다.
code
를 앞에서부터 읽으면서 만약 문자가 "1"이면 mode
를 바꿉니다. mode
에 따라 code
를 읽어가면서 문자열 ret
을 만들어냅니다.
mode
는 0과 1이 있으며, idx
를 0 부터 code의 길이 - 1
까지 1씩 키워나가면서 code[idx]
의 값에 따라 다음과 같이 행동합니다.
- mode가 0일 때
code[idx]
가 "1"이 아니면idx
가 짝수일 때만ret
의 맨 뒤에code[idx]
를 추가합니다.code[idx]
가 "1"이면mode
를 0에서 1로 바꿉니다.
- mode가 1일 때
code[idx]
가 "1"이 아니면idx
가 홀수일 때만ret
의 맨 뒤에code[idx]
를 추가합니다.code[idx]
가 "1"이면 mode를 1에서 0으로 바꿉니다.
문자열 code
를 통해 만들어진 문자열 ret
를 return 하는 solution 함수를 완성해 주세요.
단, 시작할 때 mode
는 0이며, return 하려는 ret
가 만약 빈 문자열이라면 대신 "EMPTY"를 return 합니다.
⛔ 제한사항
- 1 ≤ code의 길이 ≤ 100,000
code
는 알파벳 소문자 또는 "1"로 이루어진 문자열입니다.
📋 입출력 예
code | result |
"abc1abc1abc" | "acbac" |
🔖 입출력 예 설명
입출력 예 #1
- code의 각 인덱스 i에 따라 다음과 같이 mode와 ret가 변합니다.
i | code[i] | mode | ret |
0 | "a" | 0 | "a" |
1 | "b" | 0 | "a" |
2 | "c" | 0 | "ac" |
3 | "1" | 1 | "ac" |
4 | "a" | 1 | "ac" |
5 | "b" | 1 | "acb" |
6 | "c" | 1 | "acb" |
7 | "1" | 0 | "acb" |
8 | "a" | 0 | "acba" |
9 | "b" | 0 | "acba" |
10 | "c" | 0 | "acbac" |
따라서 "acbac"를 return 합니다.
🧾 기본 제공 코드
function solution(code) {
var answer = '';
return answer;
}
🗳️ 직접 풀이한 코드
function solution(code) {
const arr = code.split('');
let mode = 0;
let ret = '';
for (const [idx, code] of arr.entries()) {
if (mode === 0) {
if (code !== '1') {
if (idx % 2 === 0) {
ret += code;
}
} else {
mode = 1;
}
} else {
if (code !== '1') {
if (idx % 2 !== 0) {
ret += code;
}
} else {
mode = 0;
}
}
}
return ret.length ? ret : 'EMPTY';
}
먼저 문제 설명에서 제시한 대로 풀어봤다.
if문을 활용하여 조건에 충족되도록 순차적으로 풀었다.
다만 풀고 나서 아쉬운 마음이 들었다.
위 풀이로 코드가 정상 동작되는 것은 확인했지만
중첩된 if문들과 세로로 길어진 코드들로 인해 직관적이지 못했고 가독성이 떨어진다고 느껴져 아쉬웠다.
🗳️ 다른 사람이 풀이한 코드
function solution(code) {
let answer = '';
let mode = 0;
for (let i = 0; i < code.length; i += 1) {
if (Number(code[i]) === 1) {
mode = mode === 1 ? 0 : 1;
}
if (Number(code[i]) !== 1 && i % 2 === mode) {
answer += code[i];
}
}
return answer.length > 0 ? answer : 'EMPTY';
}
다른 사람들이 풀이한 코드들 중 가장 많은 좋아요를 받은 풀이다.
확실히 직접 풀이한 코드와 많이 비교된다는게 느껴졌다.
다른걸 다 떠나서 가독성이 매우 뛰어나다는게 보였다.
위 코드는
answer
에 문자가 추가되기 전 먼저 Number(code[i]) === 1
로 mode
를 체크하고
이후 mode
의 상태값에 따라 answer
변수에 조건이 충족되는 문자가 추가되도록 하는
가독성도 좋고 몹시 직관적인 좋은 코드라는 생각이 들었다.
위 코드를 보고서 다시 풀어보고 싶었다.
그래서 직접 풀었던 코드를 한 번 엎었다.
참고로 봤던 위 코드를 머릿 속에 담아두고 코드를 이해해보기 위해
직접 다시 풀어보면서 기억을 더듬으며 순차적으로 재차 풀어봤다.
🗳️ 다시 풀어본 코드
function solution(code) {
let mode = 0;
let ret = '';
for (let idx = 0; idx < code.length; idx++) {
if (code[idx] === '1') {
mode = mode === 0 ? 1 : 0;
}
if (code[idx] !== '1') {
if (mode === 0 && idx % 2 === 0) {
ret += code[idx];
}
if (mode === 1 && idx % 2 === 1) {
ret += code[idx];
}
}
}
return ret.length ? ret : 'EMPTY';
}
for... of
반복문 사용을 위해 생성했던 const arr = code.split('');
배열을 삭제하고
효율성을 높이기 위해 간단한 for
반복문으로 수정했다.
그리고 과도하게 중첩됐던 if
문을 줄이고 위와 같이 분류하여 구분지었다.
순탄하게 잘 작성해보던 중 막히는 부분이 있었다.
if (code[idx] !== '1') {
if (mode === 0 && idx % 2 === 0) {
ret += code[idx];
}
if (mode === 1 && idx % 2 === 1) {
ret += code[idx];
}
}
위 코드는 mode
의 상태값에 따라 홀수번째 문자가 추가될지 짝수번째 문자가 추가될지
처리되는 코드다.
코드를 보면 알 수 있듯이 두 조건식의 구조가 매우 동일하다.
위 조건식의 구조가 동일한 건 알겠고 이를 어떻게 취합하면 좋을지 감이 오지 않았다.
한 번 비교해보면서 공통되는 부분을 찾아 취합하고 싶었다.
그래서 위 코드를 취합해보기 위해 두 조건식을 놓고 곰곰히 생각해봤다.
비교해보던 중 공통되면서도 유일하게 다른 부분을 찾을 수 있었다.
바로 mode
의 비교값과 idx % 2
로 나누고 남는 값이 같다는 거였다.
그래서 이를 찾아내어 코드를 수정해 아래와 같이 수정할 수 있었다.
if (code[idx] !== '1' && idx % 2 === mode) {
ret += code[idx];
}
🗳️ 최종 반영된 코드
function solution(code) {
let mode = 0;
let ret = '';
for (let idx = 0; idx < code.length; idx++) {
if (code[idx] === '1') {
mode = mode === 0 ? 1 : 0;
}
if (code[idx] !== '1' && idx % 2 === mode) {
ret += code[idx];
}
}
return ret.length ? ret : 'EMPTY';
}
💬 마치며
다시 풀면서 느낀거지만 다시 풀어보길 잘했다는 생각이 들었다.
좋은 코드라고 생각했던 코드를 기준으로 기억을 더듬어가며 다시 풀어본거지만
직접 풀어보면서 코드를 이해할 수 있었고, 또 눈으로만 보고 지나쳤다면 간과했을 코드 구조를
직접 구현을 통해 왜 그렇게 코드가 구성되었는지를 인지하고 이해할 수 있는 시간이 됐던 거 같아 좋았다.
앞으로 다른 코드들을 짤 때도 내가 직접 짠 코드가 아쉽다면
왜 아쉬운지 그리고 그 아쉬움을 어떻게 해결할 수 있을지,
어떻게 리팩토링을 하면 더 효율적인 코드로 짤 수 있을지를 생각하면서 풀어봐야겠다는 다짐의 순간이었다.
🔸 작성된 본문 내용 중 미흡한 부분이 있을 수 있습니다.
🔸 고칠 부분이나 추가되면 좋을 내용 댓글로 남겨주시면 완성도 높은 글을 만들어가는 데에 큰 힘이 됩니다. ☺️