자바스크립트의 sort
함수는 기본적으로 원소들을 문자열로 만든 뒤에, UTF-16
코드 유닛 값을 기준으로 순서를 정렬합니다. 여기서 눈치 채신 분도 있겠지만 문자열로 바꾼 뒤에 정렬을 하기 때문에 숫자 정렬에 적합하지 않습니다.
UTF-16
코드 유닛 값으로 값을 정렬하게 되면, 숫자에 대해 올바른 정렬이 되지 않습니다. 이를테면 [1, 30, 4, 21, 100000];
을 정렬한 결과가 [1, 100000, 21, 30, 4]
와 같이 나오게 되는 현상이 발생합니다.
이러한 sort
함수의 특징 때문에 우리는 sort
함수에 대한 커스터마이징을 해주어야 합니다.
sort
함수의 기본 문법은 arr.sort([compareFunction])
입니다. 여기서 우리가 정의해줘야 하는 것은 [compareFunction]
부분입니다.
[compareFunction]
은 sort 함수의 콜백함수로 firstEl
과 secondEl
이란 인자가 자동으로 들어갑니다. 이 콜백함수의 작성을 생략하면 이전에 설명했듯 문자열로 바뀐 뒤 UTF-16
의 코드 유닛 값을 기준으로 정렬이 수행됩니다.
[compareFunction]
은 다양한 언어에서 콜백함수로 작성됩니다. 이를테면 Java와 같은 언어에서도 이러한 함수를 작성하는 부분이 있습니다.
[compareFunction]
을 작성할 때는 어떤 값을 반환하는지가 중요 합니다. 숫자 값을 반환해야 하는데 총 3가지 경우로 나눌 수 있습니다.
0을 기준으로 3가지 케이스로 나뉩니다.
a
, b
를 받았고 반환 값이 0보다 큰 경우에는 만일 [a, b]
의 값이 들어왔다면, 그대로 [a, b]
가 됩니다. a
가 먼저옵니다.a
, b
를 받았고 반환 값이 0인 경우에는 만일 [a, b]
의 값이 들어왔다면, 그대로 [a, b]
가 됩니다. a
와 b
의 위치를 그대로 둡니다. a
, b
를 받았고 반환 값이 0보다 작은 경우에 [a, b]
의 값이 들어왔다면, b
가 먼저 오게 됩니다. [b, a]
가 되는 겁니다.그래서 숫자를 정렬하는 sort
함수를 작성한다할 때, 예를들어 [1, 2, 3, 4, 5]
라는 배열이 있다고 할 때 정렬 시 모든 엘리먼트가 순서대로여서 변화가 없다고 가정하면 1
, 2
, 3
, 4
라는 값이 차례로 a로 들어갈 것이고, 2
, 3
, 4
, 5
라는 값이 차례로 b로 들어갈 것입니다. 쌍으로 묶어서 보면 [2, 1]
, [3, 2]
, [4, 3]
, [5, 4]
의 값이 차례로 a
와 b
에 들어갑니다.
그런데 일반적인 경우에는 정렬은 하는 도중에 내부의 값들이 변하기 때문에 a
, b
에 실제로는 우리가 정렬하면서 바뀌게 된 값들이 들어갈 것입니다. 만일, 새로 들어온 값이 더 크다면 바꾸게 하는 [compareFunction]
을 작성하고 싶다면 어떻게 해야 할까요?
위에 설명했던 경우의 수를 잘 생각하면 됩니다. 뒤에 들어온게 크면? 바꾸기. 바꿀때는 -1
을 리턴하면 되겠죠?
간단하게 삼항연산자로 표현하면 a > b ? -1 : 0
처럼 표현할 수 있겠네요.
여기서 헷갈릴만한 것 하나가 a와 b입니다.
sort((a, b) => b - a)
라는 소스코드와[1, 2, 3]
이라는 배열이 있을 때, 첫 루프로 a에 2가 들어오고 b에 1이 들어옵니다. 즉, a, b의 알파벳 순서와는 반대로 들어옵니다. 이게 헷갈리신다면sort((next, prev) => prev - next)
와 같은 형식으로 사용하는 것도 추천드립니다.
코드 예제를 만들어 살펴봅시다.
let someNumberArray = [5, 4, 3, 2, 1];
someNumberArray.sort((next, prev) => prev > next ? -1 : 0); // return [1, 2, 3, 4, 5]
// 이전 엘리먼트가 이후 엘리먼트보다 크면 순서를 바꾸기 때문에 결국 오름차순으로 정렬됩니다.
sort 함수는 원본을 변경하는 함수이기 때문에 매우 주의해야 합니다!
위의 소스코드 이후에 다시 someNumberArray
의 상태를 확인해보면, [1, 2, 3, 4, 5]
로 바뀌어있는 것을 알 수 있습니다. 원본도 변경하면서 결과를 반환도 하니까 알아두시면 좋을 것 같습니다.
어찌됐든, sort 함수의 핵심은 비교함수를 작성하는 것이고, 비교함수에서 a(next)
와 b(prev)
가 들어오고 무언가 바꾸고 싶을 때 -1
을 리턴하는 것! 입니다.
숫자를 정렬할 때 사실 위와 같은 삼항연산자도 필요 없다는 사실을 아시나요?
만일, 오름차 순으로 정렬하고 싶다면
arr.sort((a, b) => a - b)
와 같이 작성하면 됩니다. b
가 더 클 때만 a - b
의 결과로 음수가 나옵니다. 그러면 b
즉, 앞에 있는 값이 더 클 때 변경이 일어나니까 뒤에 있는 큰 값이 다 앞으로 오게 돼서 오름차 순 정렬이 됩니다.
만일, 내림차 순으로 정렬하고 싶다면
arr.sort((a, b) => b-a)
와 같이 작성하면 됩니다. a
(뒤에 있는 값을 의미)가 더 클 때만 b - a
의 결과로 음수가 나오겠죠? 그러면 결국 뒤에 있는 값이 클 때 변경이 일어나기 때문에, 큰 값은 뒤로 가게 되고 작은 값은 앞으로 오게 돼서 내림차순 정렬이 됩니다.
기타 예제는 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort 에서 보실 수 있습니다.
Object
와 같은 곳에 응용도 가능합니다.
정렬에 대해서 정리한 자료는 잘보았습니다 하나 의문점이 있어서 댓글을 다는데요
[오름차순 정렬]
arr.sort((a, b) => a - b)와 같이 작성하면 됩니다. b 가 더 클 때만 a - b의 결과로 음수가 나옵니다. 그러면 b 즉, 앞에 있는 값이 더 클 때 변경이 일어나니까 뒤에 있는
[[큰 값이 다 앞으로 오게 돼서]] // 해당 부분
오름차 순 정렬이 됩니다.
위에 표시한 내용이 반대로 적으신게 아닌지해서요 a-b를 해서 앞에 있는 값이 더 크면 변경이 되는데 앞에 있는 값이 더크면 뒤에 있는 작은 값이 앞으로 오게 되서 오름차순 정렬이 되는거 아닌가요?
감사합니다.