숫자 배열을 sort()로 정렬했더니 100000이 21보다 앞에 온 이유
문제 발생
조회수 순으로 정렬했는데 순서가 엉망이었습니다.
const views = [1, 30, 4, 21, 100000];
views.sort();
console.log(views); // [1, 100000, 21, 30, 4]한 자리 수만 있는 테스트 데이터에서는 정상으로 보였습니다.
원인 분석
기본 비교자는 문자열 비교입니다. MDN이 그대로 적습니다 — 기본 정렬 순서는 오름차순이며, 요소를 문자열로 변환한 다음 그 UTF-16 코드 단위 값의 순서를 비교하는 방식입니다. 문서의 예제 결과가 위와 정확히 같습니다.
문자열로 보면 "100000"은 "21"보다 앞섭니다. 첫 글자 1과 2를 비교하는 순간 결정되기 때문입니다. 테스트 데이터가 한 자리 수뿐이었다면 문자열 순서와 숫자 순서가 우연히 일치해 문제가 드러나지 않습니다.
함께 알아둘 규칙이 셋 더 있습니다.
undefined는 항상 끝으로 갑니다. 비교 함수와 무관합니다.- 정렬은 안정적입니다. ES2019부터 명세가 안정 정렬을 요구하므로, 같은 값끼리는 원래 순서가 유지됩니다.
sort()는 제자리에서 정렬하고 같은 배열의 참조를 반환합니다. 원본이 바뀝니다.
해결 방안
- 숫자에는 비교 함수를 줍니다.
views.sort((a, b) => a - b); // 오름차순
views.sort((a, b) => b - a); // 내림차순- 원본을 지켜야 하면
toSorted()를 씁니다. React state처럼 불변으로 다뤄야 하는 값에서는 이게 기본입니다.
const sorted = views.toSorted((a, b) => a - b);- 문자열 정렬에는
localeCompare를 씁니다. 기본 비교는 코드 단위 순서라 한글·악센트 문자에서 사람이 기대하는 순서와 다릅니다.
names.sort((a, b) => a.localeCompare(b, "ko"));목록이 크면 Intl.Collator를 만들어 compare를 재사용하는 편이 빠릅니다.
- 여러 기준은 앞 기준의 결과가 0일 때만 다음으로 넘깁니다.
posts.sort((a, b) => b.likes - a.likes || a.title.localeCompare(b.title, "ko"));- 비교 함수가 일관적인지 확인합니다. 같은 두 값에 대해 항상 같은 부호를 돌려줘야 합니다 — 안에서 현재 시각이나 난수를 읽으면 결과가 실행마다 달라집니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.