문제 상황
다음과 같은 숫자로 이루어진 2차원 배열이 있다고 가정해 보겠습니다.
const arr = [
[1, 3, 2],
[5, 2, 1, 4],
[2, 1]
];이 배열에서 같은 숫자끼리 모아 각각 별도의 하위 배열로 묶고, 이렇게 만들어진 하위 배열들을 오름차순으로 정렬하는 JavaScript 함수를 작성해야 합니다.
즉, 최종 결과물은 다음과 같은 형태가 되어야 합니다.
const output = [
[1, 1, 1],
[2, 2, 2],
[3],
[4],
[5]
];해결 방법
이 문제는 객체(맵)를 활용하면 효율적으로 해결할 수 있습니다. 각 숫자가 처음 등장할 때 새로운 빈 배열을 생성하고 결과 배열에 추가한 뒤, 이후 동일한 숫자가 나올 때마다 해당 배열에 값을 쌓아가는 방식입니다.
핵심 로직은 다음과 같습니다.
- 숫자를 키로 사용하는 맵(map)을 만들어 중복 확인과 접근을 O(1) 시간 복잡도로 처리합니다.
- 배열을 한 번 순회하며 각 요소를 맵의 해당 그룹에 추가합니다.
- 모든 요소 처리 후, 결과 배열을 각 그룹의 첫 번째 값을 기준으로 오름차순 정렬합니다.
구현 코드
전체 코드는 다음과 같습니다.
const arr = [
[1, 3, 2],
[5, 2, 1, 4],
[2, 1]
];
const groupAndSort = arr => {
const res = [];
const map = Object.create(null);
Array.prototype.forEach.call(arr, item => {
item.forEach(el => {
if (!(el in map)) {
map[el] = [];
res.push(map[el]);
}
map[el].push(el);
});
});
res.sort((a, b) => {
return a[0] - b[0];
});
return res;
};
console.log(groupAndSort(arr));코드 설명
- map 생성:
Object.create(null)을 사용해 프로토타입이 없는 순수 객체를 만들면,toString이나hasOwnProperty같은 내장 속성 이름과의 충돌을 걱정할 필요가 없습니다. - 그룹화: 각 숫자가 처음 나타나면 새 배열을 만들어
res에 참조로 저장하고, 이후에는 해당 배열에 계속 값을 push합니다. - 정렬: 마지막에 각 그룹의 첫 번째 요소(
a[0])를 비교하여 오름차순으로 정렬합니다. 같은 그룹 내부의 값은 이미 삽입 순서대로 동일하기 때문에 별도 정렬이 필요하지 않습니다.
실행 결과
코드를 실행하면 콘솔에 다음과 같은 출력이 나타납니다.
[ [ 1, 1, 1 ], [ 2, 2, 2 ], [ 3 ], [ 4 ], [ 5 ] ]
시간 복잡도는 전체 요소 수를 N이라 할 때 O(N log N)이며, 대부분의 비용은 최종 정렬 단계에서 발생합니다. 그룹화 자체는 선형 시간에 처리되므로, 데이터 크기가 커져도 안정적으로 동작하는 효율적인 방식입니다.