
프로그래머스 42576 : 완주하지 못한 선수 - Javascript
문제 설명
수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.
제한사항
- 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
- completion의 길이는 participant의 길이보다 1 작습니다.
- 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
- 참가자 중에는 동명이인이 있을 수 있습니다.
입출력 예
participant | completion | return |
---|---|---|
["leo", "kiki", "eden"] | ["eden", "kiki"] | "leo" |
["marina", "josipa", "nikola", "vinko", "filipa"] | ["josipa", "filipa", "marina", "nikola"] | "vinko" |
["mislav", "stanko", "mislav", "ana"] | ["stanko", "ana", "mislav"] | "mislav" |
-
예제 #1
"leo"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다. -
예제 #2
"vinko"는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다. -
예제 #3
"mislav"는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.
풀이 과정
전체 선수(participant) 중 완주한 선수(completion) 목록에 존재하지 않는 선수 한 명을 찾아내야 합니다.
시도 1
splice
와 indexOf
를 이용해 완주한 선수(completion)를 전체 선수(participant)에서 하나씩 소거한 뒤, 남은 한 선수의 이름을 반환하는 방식으로 풀어봤습니다.
function solution(participant, completion) {
// 전체 선수 목록에서 완주한 선수의 이름을 하나씩 소거
for (name of completion) {
participant.splice(participant.indexOf(name), 1);
}
// 전체 선수 목록에서 남아있는 선수의 이름을 반환
return participant[0];
}
하지만 정확도 테스트는 통과하고 시간 초과로 효율성 테스트는 통과하지 못했습니다. 배열을 수정하는 splice
가 느리기 때문에 생긴 문제인 듯 합니다.
시도 2
아래와 같이 해쉬를 이용해 문제를 다시 풀었고, 성공적으로 효율성 테스트까지 통과했습니다.
function solution(participant, completion) {
// 완주한 선수의 이름의 개수를 저장할 변수 선언
const counts = {};
// 완주한 선수의 이름의 개수를 `counts` 변수에 담기
for (name of completion) {
if (counts[name]) {
counts[name]++;
} else {
counts[name] = 1;
}
}
for (name of participant) {
if (counts[name]) {
counts[name]--; // 선수 이름이 있으면 개수 1 감소
} else {
return name; // 선수 이름이 없거나 0이면 해당 선수의 이름을 반환
}
}
}
숏코딩 (CodeGolf)
조금 더 코드를 간결하게 바꾸기 위해 조건식 부분을 수정해 아래와 같이 정리 가능합니다.
function solution(participant, completion) {
const counts = {};
for (name of completion) counts[name] = (counts[name] | 0) + 1;
for (name of participant) if (!counts[name]--) return name;
}
:::help counts
에 name
이 없을 수도 있는데 !counts[name]--
이 왜 제대로 작동하나요?
counts[name]
가 비어있을 경우 접근하면undefined
를 반환합니다.undefined
가 산술 연산자와 만나면 오류 없이NaN
으로 바뀌게 됩니다.NaN
은 논리값 거짓으로 변환되기 때문에,!NaN
은true
로 변환됩니다.
- 따라서
counts[name]
이 존재하지 않을 때,!counts[name]--
는true
으로 변환됩니다.
이러한 과정을 통해 매핑된 값이 없는 경우에도 선수의 이름을 반환하는 로직이 정상적으로 작동하게 됩니다.
:::
추가로 counts
객체 생성 라인을 제거하고 completion
배열을 해쉬맵 용도로 재사용합니다.
function solution(participant, completion) {
for (name of completion) completion[name] = (completion[name] | 0) + 1;
for (name of participant) if (!completion[name]--) return name;
}
:::help completion
는 배열인데 어떻게 키값을 인덱스로 사용 할 수 있나요?
- 자바스크립트에서는 배열 또한
Array
라는 객체이기 때문에 속성(property)을 추가할 수가 있습니다. - 따라서
completion[name]
이라는 코드가 정상적으로 작동하게 됩니다.
:::
for-of
를 map()
과 find()
메소드로 바꾸고 한줄의 코드로 합칩니다.
function solution(participant, completion) {
completion.map(name => (completion[name] = (completion[name] | 0) + 1));
return participant.find(name => !completion[name]--, completion);
}
solution = (participant, completion) =>
participant.find(
name => !completion[name]--,
completion.map(name => (completion[name] = (completion[name] | 0) + 1))
);
마지막으로 가독성을 포기하고 모든 이름을 한글자로 수정하면 아래와 같이 숏코딩 코드가 완성됩니다.
solution=(p,c)=>p.find(n=>!c[n]--,c.map(n=>(c[n]=(c[n]|0)+1)))