생각 없이 쓰던 정렬 제대로 알아보자.
평소 코딩 테스트 준비 때문에 알고리즘 문제를 자주 풀곤 하는데, sort(), apply() 같은 메서드부터 Map, Set 같은 타입까지 자바스크립트에 내장된 다양한 문법들을 그저 문제 풀기만 급급해 대충 이해하고 넘기는 기분이 들었습니다. 그러다 보니 메서드를 응용하는 문제에서는 다시 막히고, 또 풀이를 보고 암기하는 악순환이 반복되는 거죠.
그래서 일단 사용하는 함수들을 제대로 이해하는 것이 먼저라고 생각해 이번 시리즈를 기획하게 되었습니다. 이번 포스팅에서는 sort 함수의 정의 및 사용 방법, 그리고 응용 방법까지 알아보겠습니다.
sort() 메서드는 배열의 요소를 정렬하는 데 사용하는 함수입니다. 공식 문서에서는 기본 구문을 아래와 같이 정의합니다.
arr.sort([compareFunction])
대괄호([])는 괄호 안의 매개변수가 필수가 아니라 상황에 따라 생략이 가능하다는 의미입니다.
정렬의 경우 매개변수가 있을 경우와 없을 경우에 값이 달라지는 모습이 종종 보이는데요, 그 이유는 아래 공식 문서의 번역본에서 확인할 수 있습니다.
요소의 순서를 결정하는 데 사용되는 함수. 첫 번째 값이 두 번째 값보다 작을 경우 음수 값을 반환하고, 같을 경우 0을 반환하며, 그렇지 않을 경우 양수 값을 반환한다. 파라미터를 생략할 경우 요소는 ASCII 문자 기준 오름차순으로 정렬된다.
여기서 우리가 주목할 문단은 매개변수(파라미터)를 생략할 경우 요소는 ASCII 문자 기준 오름차순으로 정렬된다.입니다. 만약 매개변수 없이 그냥 sort() 함수를 실행할 경우 아래와 같은 결과를 얻습니다.
const numbers = [10, 3, 8, 4, 1];
numbers.sort();
console.log(numbers);
// 결과 : [ 1, 10, 3, 4, 8 ]
1부터 차례대로 정렬될 것이란 예상과 달리 10이 3보다 앞에 위치했습니다. 그 이유는 아스키 문자 기준으로 정렬할 때 일시적으로 숫자 타입을 문자열(String) 타입으로 형 변환하기 때문입니다. 그리고 형 변환 이후에는 첫 번째 문자를 기준으로 비교하기 때문에 문자 1이 맨 앞에 있는 "10"이 "3"보다 작은 아스키코드 값을 가질 수 있게 되는 거죠.
그렇다면 우리가 예상했던 대로 3이 10보다 앞에 있는 정렬을 하기 위해서는 어떻게 해야 할까요? 이때 필요한 개념이 앞서 기본 구문에서 봤던 compareFunciton 파라미터입니다.
compareFunciton를 선언할 경우 sort() 함수는 compareFunciton에게 배열의 요소를 2개씩 반복해서 보낸 뒤, compareFunciton이 반환하는 값을 기준으로 정렬을 합니다. 만약 보내는 요소들의 이름을 a, b라고 한다면 반환 값에 따른 sort() 함수의 해석은 아래와 같습니다.
반환 값 < 0 : a 가 b보다 앞에 있어야 한다.
반환 값 = 0 : a와 b의 순서를 바꾸지 않는다.
반환 값 > 0 : b가 a보다 앞에 있어야 한다.
이러한 메커니즘을 생각하고 오름차순 코드를 짠다면 아래와 같이 작성할 수 있습니다.
const numbers = [10, 3, 8, 4, 1];
function compare(a, b) {
return a - b;
}
numbers.sort(compare);
console.log(numbers);
// 결과 : [ 1, 3, 4, 8, 10 ]
우리는 이름이 compare인 파라미터 함수를 만들고, 인자로 받는 a에서 b를 뺀 값을 반환하게 작성했습니다. 만약 a가 1이고 b가 3이면 -2를 sort 함수에게 다시 보내주는 거죠. 그리고 sort는 compare 함수가 보낸 값이 음수인 것을 확인하고 1을 3 앞에 위치시킵니다.
이제야 우리가 생각한 정렬이 되었네요. 만약 내림차순 정렬을 해야 하는 경우엔 a - b를 b - a로 바꾸어 실행하면 됩니다.
const numbers = [10, 3, 8, 4, 1];
function compare(a, b) {
return b - a;
}
numbers.sort(compare);
console.log(numbers);
// 결과 : [ 10, 8, 4, 3, 1 ]
참고로 위 예시처럼 compare함수를 별도로 생성할 수도 있지만, a - b처럼 단순한 계산의 경우 주로 아래와 같은 익명 함수를 더 많이 사용합니다.
numbers.sort(function(a, b) {
return (a - b);
});
ES6부터 도입된 화살표 함수 (Arrow Function)을 이용하면 더 간결한 표현이 가능합니다.
numbers.sort((a, b) => a - b);
일반적으로 정렬을 배열에 사용하는 것으로 알고 있지만, compareFunciton의 개념을 이해했다면 다양한 타입에서도 사용이 가능합니다.
오브젝트 타입의 경우 정렬하고자 하는 오브젝트의 프로퍼티를 참조하여 정렬할 수 있습니다.
const market = {
name: "이마트",
fruits: [
{
name: "Banana",
price: 1000,
}, {
name: "Apple",
price: 500,
},{
name: "Cherry",
price: 100,
}
]
}
market.fruits.sort((a, b) => {
return ( a.price - b.price );
})
console.log(market);
결과
{
name: '이마트',
fruits: [
{ name: 'Cherry', price: 100 },
{ name: 'Apple', price: 500 },
{ name: 'Banana', price: 1000 }
]
}
compareFunction 안에 if문을 사용하여 정렬 조건을 직접 정의할 수도 있습니다.
아래의 코드는 요소가 0일 경우 맨 끝으로 정렬하고, 그렇지 않은 경우는 오름차순으로 정렬하는 코드입니다.
const numbers = [0, 0, 0, 3, 2, 1];
numbers.sort(function(a,b){
if (a === 0)
return 1;
else if (b === 0)
return -1;
else
return a - b;
});
console.log(numbers)
// 결과 : [ 1, 2, 3, 0, 0, 0 ]
sort 함수는 어떤 compare 함수를 작성하는지에 따라 무궁무진한 방법으로의 정렬이 가능한 메서드입니다. 저 또한 이번 기회로 sort() 함수가 정확히 어떤 메커니즘으로 작동하지는지 이해할 수 있었습니다.
사실과 다른 내용이 있거나 덧붙이고 싶은 내용이 있으시다면 언제든 댓글로 남겨주시길 바랍니다.
그럼 다음 포스팅에서 뵙겠습니다.
Danny Goodman, [JavaScript Bible], John Wiley & Sons Inc, 2007, 963~966p