관련지식
javascript, node.js, sort, readdir

디렉토리에 아래와 같이 파일이 있다고 가정합니다.

윈도우에서는 숫자로 시작하는 파일명에 대해 숫자 순서대로 정렬해서 보여주지만, 이 목록을 node.js 에서 readdir() 함수로 조회를 한다면 결과는 달라집니다.

한글이나 영문으로 시작하는 파일은 무시하더라도, 100으로 시작하는 파일이 24 보다 앞으로 있습니다. 이런 경우에 정렬하는 기능을 만들어 보겠습니다.

편의상 아래와 같은 샘플 데이터를 사용하겠습니다.

  1. let list = [ '01 [node.js] sharp 패키지 써보기.md',
  2. '02 [node.js] sharp 패키지로 이미지 자동 분할.md',
  3. '03 [node.js] async 패키지 써보기.md',
  4. '10 [javascript] filter map reduce 골라쓰기.md',
  5. '100 [tool] 현재 접속한 단말기의 공인 아이피 확인하기.md',
  6. '24 [javascript] Request Referer 와 관련한 몇 가지 사항.md',
  7. 'sandbox 속성.md',
  8. 'this.md',
  9. '[리뷰] Bluetooth converter usb hub from wired keyboard mouse to wireless keyboard mouse.md',
  10. '[에디터 제작기] 3.커서 세팅하기.md',
  11. '유입키워드.md' ];

정렬

Javascript의 배열을 정렬하기 위해선 배열 객체에서 제공하는 sort() 함수에 정렬 기준이 되는 비교함수를 작성하면 됩니다.

  1. list = list.sort(function(val1, val2) {
  2. //비교 로직 필요
  3. });

비교함수에서 리턴하는 음수, 0, 양수 값에 따라 정렬 방향이 달라질 것입니다.

먼저 파일명에서 앞의 숫자 부분만 잘라내서 크기를 비교해야 합니다.

  1. let a = parseInt(val1.split(' ', 1)[0]);
  2. let b = parseInt(val2.split(' ', 1)[0]);

변수 a 와 b 는 숫자로 변환했기 때문에 숫자로서의 크고 작음으로 비교가 가능합니다.

  1. return a < b ? -1 : a > b ? 1 : 0; //숫자로 시작하는 파일끼리 오름차순으로

그런데 숫자로 시작하지 않는 파일도 있습니다. 이 경우엔 문자로 비교해야 합니다. 숫자가 아닌것을 parseInt() 로 형변환하게 되면 NaN(Not a Number) 가 됩니다. 따라서 그러한 경우엔 Number.isNaN() 함수로 확인하고 형변환된 값이 아니라 원래의 문자열 값으로 비교릃 해야 합니다.

  1. if(Number.isNaN(a) || Number.isNaN(b)) //둘중의 하나가 숫자로 시작하는 파일이 아닐때
  2. return val1 < val2 ? -1 : val1 > val2 ? 1 : 0; //문자열 파일이름으로 비교
  3. return a < b ? -1 : a > b ? 1 : 0; //숫자로 시작하는 파일끼리 오름차순으로

그런데 정렬을 위한 비교 문법이 보기에 안좋습니다. 함수화하여 좀더 예쁘게 바꿔보겠습니다. 먼저 아래와 같이 함수와 변수를 추가합니다.

  1. const Order = {
  2. ASC : 1,
  3. DESC : -1
  4. };
  5. function order(v1, v2, ord) {
  6. return v1 < v2 ? ord * -1 : v1 > v2 ? ord : 0;
  7. }

그리고 기존 비교식을 수정합니다.

  1. if(Number.isNaN(a) || Number.isNaN(b)) //둘중의 하나가 숫자로 시작하는 파일이 아닐때
  2. return order(val1, val2, Order.ASC); //문자열 파일이름으로 비교
  3. return order(a, b, Order.ASC); //숫자로 시작하는 파일끼리 오름차순으로

현재까지의 조건으로 정렬을 하면 아래와 같이 출력됩니다. 반대로 정렬하고 싶을 경우 Order.ASC 을 Order.DESC로 수정하면 됩니다. 매우 간단하죠.

  1. [ '01 [node.js] sharp 패키지 써보기.md',
  2. '02 [node.js] sharp 패키지로 이미지 자동 분할.md',
  3. '03 [node.js] async 패키지 써보기.md',
  4. '10 [javascript] filter map reduce 골라쓰기.md',
  5. '24 [javascript] Request Referer 와 관련한 몇 가지 사항.md',
  6. '100 [tool] 현재 접속한 단말기의 공인 아이피 확인하기.md',
  7. '[리뷰] Bluetooth converter usb hub from wired keyboard mouse to wireless keyboard mouse.md',
  8. '[에디터 제작기] 3.커서 세팅하기.md',
  9. 'sandbox 속성.md',
  10. 'this.md',
  11. '유입키워드.md' ]

정렬 확장

현재까지 만들어진 기능에선 문자열 파일명의 정렬 방식이 숫자로 시작하는 파일명 정렬에도 영향을 줍니다.

return order(val1, val2, Order.ASC); 일때)

  1. [ '01 [node.js] sharp 패키지 써보기.md',
  2. '02 [node.js] sharp 패키지로 이미지 자동 분할.md',
  3. '03 [node.js] async 패키지 써보기.md',
  4. '10 [javascript] filter map reduce 골라쓰기.md',
  5. '24 [javascript] Request Referer 와 관련한 몇 가지 사항.md',
  6. '100 [tool] 현재 접속한 단말기의 공인 아이피 확인하기.md',
  7. '[리뷰] Bluetooth converter usb hub from wired keyboard mouse to wireless keyboard mouse.md',
  8. '[에디터 제작기] 3.커서 세팅하기.md',
  9. 'sandbox 속성.md',
  10. 'this.md',
  11. '유입키워드.md' ]

return order(val1, val2, Order.DESC); 일때)

  1. [ '유입키워드.md',
  2. 'this.md',
  3. 'sandbox 속성.md',
  4. '[에디터 제작기] 3.커서 세팅하기.md',
  5. '[리뷰] Bluetooth converter usb hub from wired keyboard mouse to wireless keyboard mouse.md',
  6. '01 [node.js] sharp 패키지 써보기.md',
  7. '02 [node.js] sharp 패키지로 이미지 자동 분할.md',
  8. '03 [node.js] async 패키지 써보기.md',
  9. '10 [javascript] filter map reduce 골라쓰기.md',
  10. '24 [javascript] Request Referer 와 관련한 몇 가지 사항.md',
  11. '100 [tool] 현재 접속한 단말기의 공인 아이피 확인하기.md' ]

숫자로 시작하는 파일명을 앞으로 배치하고, 일반파일명은 역정렬을 하는 정렬이 불가능합니다. 따라서 조건식을 세분화해서 숫자파일끼리 정렬, 문자파일끼리 정렬, 숫자파일명과 문자파일명의 배치를 선택할수 있도록 수정하겠습니다.

  1. if(Number.isNaN(a) && Number.isNaN(b))
  2. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  3. else if(Number.isNaN(a)) //왼쪽이 문자열 파일이름일때
  4. return 1;
  5. else if(Number.isNaN(b)) //오른쪽이 문자열 파일이름일때
  6. return -1;

양쪽이 문자파일일때, 한쪽만 문자파일일때를 세분화해서 체크하면 됩니다. 어렵지 않죠. 그런데 order() 함수가 사용되지 않는 부분이 신경쓰이네요. 저 부분도 order() 함수를 사용할수 있도록 수정하겠습니다.

먼저 아래와 같이 계산식을 추가합니다.

  1. let ret = (Number.isNaN(a) ? 1 : 2) * (Number.isNaN(b) ? -1 : 2);

ret의 값이 -1, -2, 2, 4 로 올수 있는게 그 값을 기준으로 파일명의 유형을 구분할수 있습니다. 그럼 조건식도 아래와 같이 수정됩니다.

  1. if(ret == -1)
  2. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  3. else if(ret == 4)
  4. return order(a, b, Order.ASC); //숫자로 시작하는 파일끼리 오름차순으로
  5. else
  6. return ret * Order.ASC; //숫자로 시작하는 파일을 먼저

기본적으로 오름차순으로 정렬하는 로직이 완성되었습니다. 이제 원하는 형태로 정렬이 필요하다면 Order.ASC 를 Order.DESC 로 수정하면 됩니다. 문자열 파일명-숫자파일명 순으로 정렬하고, 문자열 파일명은 오름차순, 숫자 파일명은 내림차순으로 하고 싶은 경우 아래와 같이 되겠네요.

  1. if(ret == -1)
  2. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  3. else if(ret == 4)
  4. return order(a, b, Order.DESC); //숫자로 시작하는 파일끼리 내림차순으로
  5. else
  6. return ret * Order.DESC; //숫자로 시작하는 파일을 뒤로

Array.prototype.sort()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

최종 샘플

  1. let list = [ '01 [node.js] sharp 패키지 써보기.md',
  2. '02 [node.js] sharp 패키지로 이미지 자동 분할.md',
  3. '03 [node.js] async 패키지 써보기.md',
  4. '10 [javascript] filter map reduce 골라쓰기.md',
  5. '100 [tool] 현재 접속한 단말기의 공인 아이피 확인하기.md',
  6. '24 [javascript] Request Referer 와 관련한 몇 가지 사항.md',
  7. 'sandbox 속성.md',
  8. 'this.md',
  9. '[리뷰] Bluetooth converter usb hub from wired keyboard mouse to wireless keyboard mouse.md',
  10. '[에디터 제작기] 3.커서 세팅하기.md',
  11. '유입키워드.md' ];
  12. const Order = {
  13. ASC : 1,
  14. DESC : -1
  15. };
  16. function order(v1, v2, ord) {
  17. return v1 < v2 ? ord * -1 : v1 > v2 ? ord : 0;
  18. }
  19. list = list.sort(function(val1, val2) {
  20. let a = parseInt(val1.split(' ', 1)[0]);
  21. let b = parseInt(val2.split(' ', 1)[0]);
  22. //완성1
  23. /*
  24. if(Number.isNaN(a) || Number.isNaN(b)) //둘중의 하나가 숫자로 시작하는 파일이 아닐때
  25. return order(val1, val2, Order.DESC); //문자열 파일이름으로 비교
  26. return order(a, b, Order.ASC); //숫자로 시작하는 파일끼리 오름차순으로
  27. */
  28. //완성2
  29. /*
  30. if(Number.isNaN(a) && Number.isNaN(b))
  31. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  32. else if(Number.isNaN(a)) //왼쪽이 문자열 파일이름일때
  33. return 1;
  34. else if(Number.isNaN(b)) //오른쪽이 문자열 파일이름일때
  35. return -1;
  36. return order(a, b, Order.ASC); //숫자로 시작하는 파일끼리 오름차순으로
  37. */
  38. //최종완성
  39. /*
  40. let ret = (Number.isNaN(a) ? 1 : 2) * (Number.isNaN(b) ? -1 : 2);
  41. if(ret == -1)
  42. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  43. else if(ret == 4)
  44. return order(a, b, Order.ASC); //숫자로 시작하는 파일끼리 오름차순으로
  45. else
  46. return ret * Order.ASC; //숫자로 시작하는 파일을 먼저
  47. */
  48. //문자열 파일명-숫자파일명 순으로 정렬하고, 문자열 파일명은 오름차순, 숫자 파일명은 내림차순
  49. let ret = (Number.isNaN(a) ? 1 : 2) * (Number.isNaN(b) ? -1 : 2);
  50. if(ret == -1)
  51. return order(val1, val2, Order.ASC); //문자열 파일끼리 오름차순으로
  52. else if(ret == 4)
  53. return order(a, b, Order.DESC); //숫자로 시작하는 파일끼리 내림차순으로
  54. else
  55. return ret * Order.DESC; //숫자로 시작하는 파일을 뒤로
  56. });
  57. console.log(list);