JavaScript 개발을 하다 보면 객체 배열을 미리 정의된 순서에 따라 정렬해야 하는 경우가 종종 있습니다. 대표적인 예로, 주문 목록을 상태별 우선순위 순서대로 나열해야 하는데, 그 우선순위가 별도의 문자열 배열에 담겨 있는 경우를 들 수 있습니다.
문제 상황
다음과 같이 객체 배열과 문자열 배열이 있다고 가정해 보겠습니다.
const orders = [
{ status: "pending" },
{ status: "received" },
{ status: "sent" },
{ status: "pending" }
];
const statuses = ["pending", "sent", "received"];우리가 작성해야 할 함수는 이 두 배열을 입력받아, orders 배열을 statuses 배열의 요소 순서에 맞춰 정렬하는 역할을 합니다. 즉, 첫 번째 배열의 객체들이 두 번째 배열에 나열된 문자열의 순서대로 배치되어야 합니다. 위 예시에서는 'pending' → 'sent' → 'received' 순으로 정렬되어야 합니다.
해결 방법: sort()와 indexOf() 활용
Array.prototype.sort() 메서드에 비교 함수(comparator)를 전달하면 이 문제를 간단히 해결할 수 있습니다. 비교 함수 내부에서 각 객체의 status 값이 참조 배열인 statuses에서 어느 인덱스에 위치하는지 indexOf()로 확인한 뒤, 두 인덱스의 차이를 반환하면 참조 배열의 순서대로 정렬이 완료됩니다.
const orders = [
{ status: "pending" },
{ status: "received" },
{ status: "sent" },
{ status: "pending" }
];
const statuses = ["pending", "sent", "received"];
const sortByRef = (orders, statuses) => {
const sorter = (a, b) => {
return statuses.indexOf(a.status) - statuses.indexOf(b.status);
};
orders.sort(sorter);
};
sortByRef(orders, statuses);
console.log(orders);실행 결과
코드를 실행하면 콘솔에 다음과 같이 출력됩니다. 'pending' 객체 두 개가 가장 앞에 위치하고, 이어서 'sent', 마지막에 'received'가 오는 것을 확인할 수 있습니다.
[
{ status: 'pending' },
{ status: 'pending' },
{ status: 'sent' },
{ status: 'received' }
]성능 개선 팁: Map 사용하기
배열 크기가 큰 경우에는 주의할 점이 있습니다. 위 방식은 비교 함수가 호출될 때마다 indexOf()가 실행되어 선형 탐색(O(n))이 반복되므로, 데이터가 많아지면 정렬 성능이 저하될 수 있습니다. 이런 경우 정렬 전에 Map 객체에 각 상태의 인덱스를 미리 저장해 두면 O(1) 시간에 조회할 수 있어 훨씬 효율적입니다.
const indexMap = new Map(statuses.map((s, i) => [s, i])); orders.sort((a, b) => indexMap.get(a.status) - indexMap.get(b.status));
참고로 sort()는 원본 배열을 직접 변경(mutation)하므로, 원본을 유지해야 한다면 [...orders].sort(...)처럼 복사본을 정렬하는 것이 좋습니다.