JavaScript에서 리터럴 값을 담은 배열을 입력받아, 같은 값이 3개씩 묶여 반복되는 항목을 모두 필터링한 새로운 배열을 반환하는 함수를 작성해야 하는 경우가 있습니다. 즉, 특정 값이 3번 이상 연속해서 나타나면 3개 단위로 제거하고, 남은 개수(개수 % 3)만큼의 요소만 결과 배열에 포함시키는 것입니다.
동작 방식
이 문제는 크게 두 단계로 해결할 수 있습니다.
- 1단계:
reduce()를 활용해 각 값이 몇 번 등장했는지 해시 객체(hash map)에 카운트합니다. - 2단계: 각 값의 등장 횟수를 3으로 나눈 나머지(
% 3)만큼만 결과 배열에 추가합니다.
구현 코드
const arr1 = [1,1,1,3,3,5];
const arr2 = [1,1,1,1,3,3,5];
const arr3 = [1,1,1,3,3,3];
const arr4 = [1,1,1,1,3,3,3,5,5,5,5,5,5,5,5,5,5,5,5,7,7];
const removeTriplets = arr => {
// 각 값의 등장 횟수를 세는 해시맵 생성
const hashMap = arr => arr.reduce((acc, val) => {
if(val in acc){
acc[val]++;
}else{
acc[val] = 1;
}
return acc;
}, {});
let res = [];
let obj = hashMap(arr);
// 3으로 나눈 나머지만큼만 결과에 추가
for(let key in obj){
for(let i = 0; i < obj[key] % 3; i++){
res.push(key);
}
}
return res;
}
console.log(removeTriplets(arr1));
console.log(removeTriplets(arr2));
console.log(removeTriplets(arr3));
console.log(removeTriplets(arr4));실행 결과
[ '3', '3', '5' ] [ '1', '3', '3', '5' ] [] [ '1', '7', '7' ]
결과 분석
각 입력 배열에 대한 처리 결과는 다음과 같습니다.
arr1: 1이 3개라 모두 제거되고, 3은 2개, 5는 1개가 남습니다.arr2: 1이 4개이므로 3개는 제거되고 1개만 남고, 3은 2개, 5는 1개가 유지됩니다.arr3: 1과 3이 각각 3개씩 있어 전부 제거되므로 빈 배열이 반환됩니다.arr4: 1이 4개(→ 1개 남음), 3이 3개(→ 전부 제거), 5가 16개(→ 15개 제거 후 1개 남음), 7이 2개(→ 그대로 유지)입니다.
주의할 점
위 코드에서 for...in 루프로 객체의 키를 순회하면 키가 문자열로 처리되기 때문에, 결과 배열의 요소도 문자열('3', '5' 등)로 반환됩니다. 숫자 타입을 유지하고 싶다면 res.push(Number(key))처럼 명시적으로 변환해 주는 것이 좋습니다.