문제 상황
다음과 같은 문자열 리터럴 배열이 있다고 가정해 보겠습니다.
const arr = ['1185 Design','3 D Exhibits','44Doors', '4Concepts','ABC Data','acceleration'];
이 배열을 제자리(in-place)에서 정렬하는 JavaScript 함수를 작성해야 합니다. 단, 단순한 사전순 정렬이 아니라 다음과 같은 규칙을 따라야 합니다.
- 특수문자나 숫자로 시작하는 문자열이 가장 앞에 위치합니다.
- 그 뒤의 문자열들은 알파벳 순서대로 정렬됩니다.
- 같은 알파벳으로 시작하지만 대소문자가 다른 경우(예: 'a'와 'A'), 소문자로 시작하는 문자열이 먼저 옵니다.
구현 예제
위 조건을 만족하는 코드는 다음과 같습니다.
const arr = ['1185 Design','3 D Exhibits','44Doors', '4Concepts','ABC Data','acceleration'];
const caseSensitiveSort = (arr = []) => {
const sorter = (a, b) => {
if (a === b){
return 0
};
if (a.charAt(0) === b.charAt(0)){
return sorter(a.slice(1), b.slice(1))
}
if(a.charAt(0).toLowerCase() === b.charAt(0).toLowerCase()){
if(/^[a-z]/.test(a.charAt(0)) && /^[A-Z]/.test(b.charAt(0))){
return -1;
};
if(/^[a-z]/.test(b.charAt(0)) && /^[A-Z]/.test(a.charAt(0))){
return 1;
};
};
return a.localeCompare(b);
};
arr.sort(sorter);
}
caseSensitiveSort(arr);
console.log(arr);실행 결과
콘솔에 출력되는 결과는 다음과 같습니다.
[
'1185 Design',
'3 D Exhibits',
'44Doors',
'4Concepts',
'acceleration',
'ABC Data'
]코드 동작 원리
핵심은 sorter라는 이름의 재귀 비교 함수입니다. 각 단계의 로직은 다음과 같습니다.
- 완전히 같은 경우: 두 문자열이 동일하면 0을 반환하여 순서를 유지합니다.
- 첫 글자가 같은 경우:
slice(1)로 첫 글자를 제거한 나머지 부분을 재귀적으로 비교합니다. - 대소문자만 다른 경우: 첫 글자를 모두 소문자로 바꿨을 때 같다면, 정규식(
/^[a-z]/,/^[A-Z]/)으로 어느 쪽이 소문자인지 판별한 뒤 소문자 쪽이 앞에 오도록 -1 또는 1을 반환합니다. - 그 외의 경우:
localeCompare()를 사용해 기본적인 사전순 비교를 수행합니다. 이 덕분에 숫자·특수문자가 자연스럽게 알파벳보다 앞에 배치됩니다.
이처럼 비교 함수를 직접 정의하면 기본 sort() 메서드로는 처리하기 까다로운 대소문자 구분 정렬도 손쉽게 구현할 수 있습니다.