문자열이나 숫자 리터럴로 구성된 배열에 다음과 같이 중복된 값이 포함되어 있다고 가정해 보겠습니다.
const array = ['day', 'night', 'afternoon', 'night', 'noon', 'night', 'noon', 'day', 'afternoon', 'day', 'night'];
여기서 우리는 groupSimilar()라는 함수를 작성해야 합니다. 이 함수는 위와 같은 배열을 입력받아, 반복되는 요소들을 하위 배열의 첫 번째 요소로 묶고, 원본 배열에서 해당 값이 등장한 총 횟수를 두 번째 요소로 담은 새로운 배열을 반환해야 합니다.
따라서 위 예제의 경우 기대되는 출력 결과는 다음과 같습니다.
[
[ 'day', 3 ],
[ 'night', 4 ],
[ 'afternoon', 2 ],
[ 'noon', 2 ]
]구현 예제
이제 이 함수의 코드를 직접 작성해 보겠습니다. 누산기를 활용해 새로운 배열을 구성하기 위해 Array.prototype.reduce() 메서드를 사용하고, 배열 내 반복되는 요소를 효율적으로 추적하기 위해 Map 객체를 함께 활용합니다.
const array = ['day', 'night', 'afternoon', 'night', 'noon', 'night',
'noon', 'day', 'afternoon', 'day', 'night'];
const groupSimilar = arr => {
return arr.reduce((acc, val) => {
const { data, map } = acc;
const ind = map.get(val);
if(map.has(val)){
data[ind][1]++;
} else {
map.set(val, data.push([val, 1])-1);
}
return { data, map };
}, {
data: [],
map: new Map()
}).data;
};
console.log(groupSimilar(array));동작 원리
이 코드의 핵심 로직은 다음과 같습니다.
- data: 최종적으로 반환될 그룹화된 배열입니다. 각 요소는
[값, 개수]형태의 하위 배열입니다. - map: 각 값이
data배열의 몇 번째 인덱스에 저장되어 있는지 추적합니다. 덕분에 이미 등장한 값인지 확인할 때 전체 배열을 순회할 필요 없이 O(1) 시간 복잡도로 처리할 수 있습니다. - 배열을 순회하며 값이 처음 등장하면
data에 새 하위 배열을 추가하고 인덱스를 Map에 기록하고, 이미 존재하는 값이라면 해당 하위 배열의 카운트만 증가시킵니다.
출력 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[ [ 'day', 3 ], [ 'night', 4 ], [ 'afternoon', 2 ], [ 'noon', 2 ] ]
이처럼 reduce()와 Map을 조합하면 배열의 중복 값을 손쉽게 그룹화하면서 각 값의 출현 횟수까지 한 번의 순회로 계산할 수 있습니다.