JavaScript에는 많은 정렬 함수가 있지만, 우리가 흔히 보는 파일 정렬 순서와는 결과가 좀 다르다.
Electron으로 파일 관련 프로젝트를 개발하다 파일 정렬 때문에 골머리를 앓았다.
앞서 이야기한 것처럼JavaScript에는 많은 정렬 함수가 있지만 윈도우 탐색기에서 보는 정렬 순서와는 결과가 좀 다르다.
JavaScript로는 이런 결과가 흔히 나타난다.
var arr = ["filename 0.txt","filename 1.txt","filename 9.txt","filename 10.txt","filename 11.txt"];
arr.sort();
console.log(arr);
// 0: "filename 0.txt"
// 1: "filename 1.txt"
// 2: "filename 10.txt"
// 3: "filename 11.txt"
// 4: "filename 9.txt"
9가 10보다 뒤로 간다!
이걸 볼 때마다 뒷목을 잡으면서 쓰러지고 싶어진다.
JavaScript의 Array.sort 함수는 매개변수로 정렬 순서에 사용할 함수를 넘길 수 있다.
그렇기 때문에 https://tonks.tistory.com/124 같은 많은 정렬 순서 함수들이 넘쳐난다.
하지만 결과는 위 테스트 결과와 크게 다르지 않다.
왜냐하면 Windows 탐색기는 자연 정렬 순서라고 하는 별도의 정렬 방식이 있기 때문에 단순한 정렬 함수로는 차이가 나올 수 밖에 없다.
Windows 탐색기는 API 함수 StrCmpLogicalW()
기능을 사용하여 정렬을 수행한다고 한다. (참고링크 1, 2)
WindowsExplorerSort 알고리즘은 크게 아래와 같은 규칙을 따른다고 한다.
- 파일 이름은 부분적으로 비교된다. (숫자와 공백, 나머지가 따로따로 비교된다)
- 파일 이름의 숫자는 비교 가능한 숫자로 간주한다. (string이 아닌 int로 비교한다.)
- 숫자는 숫자형으로 비교되지만 같으면 더 긴 문자열이 우선된다. (filename00.txt, filename0.txt 순서임)
- 만약 숫자와 숫자가 아닌 부분을 비교하게 되면 둘다 텍스트로 간주한다.
- 텍스트는 대소문자를 구분하지 않고 비교된다.
이러한 규칙을 조금이라도 따라하기 위해선 localeCompare를 사용하면 된다. (참고링크 3)
단, 많은 수의 문자열을 정렬 할 때 성능을 높이기 위해서는 Intl.Collator
객체 를 만들고 해당 compare
속성에서 제공하는 함수 를 사용하는 것이 좋다고 한다. (참고링크 4)
var collator = new Intl.Collator('en', {numeric: true, sensitivity: 'base'});
var arr = ["filename 0.txt","filename 1.txt","filename 9.txt","filename 10.txt","filename 11.txt"];
console.log(arr.sort(collator.compare));
// 0: "filename 0.txt"
// 1: "filename 1.txt"
// 2: "filename 9.txt"
// 3: "filename 10.txt"
// 4: "filename 11.txt"
드디어!
좀 봐줄만하게 나왔다.
다만 WindowsExplorerSort 알고리즘 중 3번째는 적용이 되지 않는다.
현재 코드로는 짧은 문자열이 우선되고 있다.
var collator = new Intl.Collator('en', {numeric: true, sensitivity: 'base'});
var arr = ["filename 0.txt","filename 1.txt","filename 9.txt","filename 10.txt","filename 11.txt","filename00.txt","filename 00.txt","FILENAME 2.txt","새 텍스트.txt"];
console.log(arr.sort(collator.compare));
// 0: "filename 0.txt"
// 1: "filename 00.txt"
// 2: "filename 1.txt"
// 3: "FILENAME 2.txt"
// 4: "filename 9.txt"
// 5: "filename 10.txt"
// 6: "filename 11.txt"
// 7: "filename00.txt"
// 8: "새 텍스트.txt"
이건 좀 아쉬운 부분이다.
그래도 이것만 빼면 윈도우 탐색기와 제일 유사하게 나온다.
실제 WindowsExplorerSort는 아래와 같다
영어가 우선이고 한글이 그 다음에 오는데, 한글을 위로 올리고 싶으면 아래 코드를 사용하면 된다.
var collator = new Intl.Collator('kr', {numeric: true, sensitivity: 'base'}); // kr 명시
var arr = ["filename 0.txt","filename 1.txt","filename 9.txt","filename 10.txt","filename 11.txt","filename00.txt","filename 00.txt","FILENAME 2.txt","새 텍스트.txt"];
console.log(arr.sort(collator.compare));
// 0: "새 텍스트.txt"
// 1: "filename 0.txt"
// 2: "filename 00.txt"
// 3: "filename 1.txt"
// 4: "FILENAME 2.txt"
// 5: "filename 9.txt"
// 6: "filename 10.txt"
// 7: "filename 11.txt"
// 8: "filename00.txt"
여기서 실수할 수 있는데,
테스트 정렬이 잘 된다고 이 코드를 그냥 붙여넣으면 안 된다.
테스트와 동일하게 배열이 key 없이 value만 있으면 이 코드를 그대로 사용해도 무방하지만
실제 파일 리스트를 담고 있는 File 객체는 배열이 아닌 객체다!
따라서 File 객체에 담긴 실제 파일명을 비교해줘야 한다. (File.name
)
혹은 파일의 전체 경로인 File.path
도 괜찮다.
Electron에선 File 객체에 path 속성도 제공해주고 있어 난 path를 사용했다.
File.name
혹은 File.path
를 정렬하는 코드는 아래 코드를 사용하면 된다.
// var files = 실제 파일 배열
var collator = new Intl.Collator('en', {numeric: true, sensitivity: 'base'});
files = files.sort((a, b) => collator.compare(a.name, b.name)) // 혹은 a.path, b.path
참고 링크.
1. https://stackoverflow.com/questions/23205020/java-sort-strings-like-windows-explorer
2. https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-strcmplogicalw
3. https://stackoverflow.com/questions/2802341/javascript-natural-sort-of-alphanumerical-strings
5. https://codepen.io/TimPietrusky/pen/rKzoGN
'개발 > Frontend' 카테고리의 다른 글
[Vue] pinia store로 테마 관리하기 (0) | 2024.04.28 |
---|---|
[CSS] 다크모드 완벽 지원하기 (0) | 2024.01.24 |