정렬(sort)은 값을 정한 차례대로 다시 놓는 일입니다. 배열 메서드 toSorted()는 정렬한 새 배열을 돌려주고, sort()는 같은 배열을 제자리에서 정렬한 뒤 그 배열을 돌려줍니다.
가격을 싼 것부터 보려고 [900, 4500, 700]을 정렬하면 두 곳에서 어긋나기 쉽습니다. 처음 적은 차례가 남는지, 그리고 700이 정말 맨 앞에 오는지입니다.
이 레슨은 두 메서드가 돌려준 배열에 이름이 어떻게 붙는지 따라가고, 수를 크기 차례로 놓는 비교 함수를 씁니다.
1toSorted는 정렬한 새 배열을 돌려준다
그림의 1·2번 줄에 찍는 줄을 더했습니다.
const prices = [900, 4500, 700];
const low = prices.toSorted();
console.log(low);
console.log(prices);
[ 4500, 700, 900 ]
[ 900, 4500, 700 ]toSorted()는 prices의 값을 정렬해 담은 새 배열을 만들어 돌려줍니다. 2번 줄은 그 새 배열에 low를 붙이고, prices는 처음 차례 그대로입니다. map·filter·find의 map처럼 처음 배열을 건드리지 않습니다.
그런데 맨 앞이 700이 아니라 4500입니다. 비교 함수를 넘기지 않으면 toSorted()는 값을 글자로 바꿔 견줍니다. 같음과 비교에서 본 글자 견주기처럼 첫 글자부터 견주니, '4500'의 '4'가 '7'·'9'보다 앞입니다.
이름을 low라고 지어도 컴퓨터는 그 뜻을 모릅니다(규칙 6). 수를 크기 차례로 놓는 법은 3절에서 봅니다.
2sort는 같은 배열을 제자리에서 바꾼다
그림의 3번 줄을 떼어 찍는 줄을 더했습니다. 이번에는 sort()를 부릅니다.
const prices = [900, 4500, 700];
const result = prices.sort();
console.log(prices);
console.log(result === prices);
[ 4500, 700, 900 ]
truesort()는 새 배열을 만들지 않고, prices가 가리키는 배열의 칸을 제자리에서 다시 놓습니다. 배열 바꾸기와 복사의 push()처럼 같은 배열이 바뀝니다.
그리고 정렬한 그 배열을 돌려줍니다. 그래서 result와 prices는 같은 배열을 가리킵니다. 한 배열에 이름이 둘 붙었습니다(규칙 3). 배열을 === 연산자로 견주면 같은 배열 하나인지를 물으니(배열과 이름 둘), 4번 줄은 true를 찍습니다.
두 메서드를 견주면 다음과 같습니다.
toSorted() | sort() | |
|---|---|---|
| 처음 배열 | 그대로 | 제자리에서 바뀜 |
| 돌려주는 값 | 새 배열 | 같은 배열 |
처음 차례를 다시 써야 하면 toSorted()를 씁니다. 처음 차례가 더는 필요 없을 때만 sort()로 제자리에서 정렬합니다.
3비교 함수 — 수를 크기 차례로
비교 함수 없이 수를 정렬하는 코드를 하나 더 봅니다. 무엇이 찍힐지 정한 뒤 펼쳐 보세요.
const prices = [
4500,
900,
10000,
2000,
];
console.log(prices.sort());
실행 결과 보기
[ 10000, 2000, 4500, 900 ]예상과 다르면 그곳이 배울 곳입니다. 글자로 견주니 첫 글자 '1'·'2'·'4'·'9' 차례입니다. 가장 큰 10000이 맨 앞에, 가장 작은 900이 맨 끝에 옵니다.
수의 크기로 정렬하려면 비교 함수(compare function)를 넘깁니다. 비교 함수는 값 둘 a·b를 받아 수 하나를 돌려줍니다. 그 수가 0보다 작으면 a를 앞에, 0보다 크면 b를 앞에 놓고, 0이면 두 값의 차례를 그대로 둡니다.
a - b는 a가 작을 때 0보다 작으니, 작은 값이 앞에 옵니다.
const prices = [900, 4500, 700];
const low = prices.toSorted(
(a, b) => a - b,
);
console.log(low);
[ 700, 900, 4500 ]이제 low가 이름대로 싼 값부터입니다. 비교 함수도 map·filter·find에서처럼 넘긴 함수입니다. 어느 두 값을 어떤 차례로 견줄지는 toSorted()가 정하니, 부르는 차례를 따라가지 않아도 됩니다. 돌려준 수가 0보다 작은지 큰지만 봅니다.
작은 값부터 놓는 차례를 오름차순, 큰 값부터 놓는 차례를 내림차순이라고 합니다. 내림차순은 (a, b) => b - a를 넘깁니다.
예제
가장 싼 값과 가장 비싼 값
const prices = [900, 4500, 700];
const low = prices.toSorted(
(a, b) => a - b,
);
console.log(low[0]);
console.log(low.at(-1));
console.log(prices);
700
4500
[ 900, 4500, 700 ]오름차순으로 정렬한 low의 0번 칸이 가장 싼 값, 마지막 칸이 가장 비싼 값입니다. prices는 처음 차례 그대로라 적은 차례대로도 쓸 수 있습니다.
값 하나만 필요하면 가장 큰 값 찾기의 틀로도 찾습니다. 정렬은 차례 전체가 필요할 때 씁니다.
점수를 높은 것부터
const scores = [72, 95, 88];
const high = scores.toSorted(
(a, b) => b - a,
);
console.log(high);
console.log(high[0]);
[ 95, 88, 72 ]
95b - a는 a가 클 때 0보다 작으니, 큰 값이 앞에 옵니다(내림차순). 0번 칸이 가장 높은 점수입니다.
자주 틀리는 곳
틀린 생각: 수 배열은 sort()만 불러도 작은 수부터 놓인다.
한 자리 수끼리는 글자 차례와 크기 차례가 같아 맞게 보입니다. 파이썬의 sorted([10, 9, 1])도 [1, 9, 10]을 돌려줍니다. 그러나 자바스크립트의 sort()는 비교 함수가 없으면 수도 글자로 견줍니다.
const cups = [10, 9, 1];
cups.sort();
console.log(cups);
[ 1, 10, 9 ]'10'은 첫 글자 '1'이 '9'보다 앞이라 9보다 먼저 옵니다.
맞는 규칙: 비교 함수가 없으면 sort()·toSorted()는 값을 글자로 바꿔 견줍니다. 수를 크기 차례로 놓으려면 (a, b) => a - b를 넘깁니다.
틀린 생각: sort()도 정렬한 새 배열을 돌려주니, 처음 배열은 그대로다.
toSorted()처럼 돌려준 값에 새 이름을 붙이면, 새 배열이 생긴 것처럼 보입니다.
const prices = [900, 4500, 700];
const low = prices.sort(
(a, b) => a - b,
);
console.log(prices);
[ 700, 900, 4500 ]low와 prices는 같은 배열을 가리킵니다. 처음 차례 [ 900, 4500, 700 ]은 남지 않았습니다.
맞는 규칙: sort()는 같은 배열을 제자리에서 정렬하고 그 배열을 돌려줍니다. 처음 차례를 남기려면 toSorted()를 씁니다.
틀린 생각: 영어 글자는 사전처럼 대문자·소문자를 가리지 않고 알파벳 차례로 놓인다.
사전은 대문자와 소문자를 가리지 않고 알파벳 차례로 싣습니다.
const letters = ['b', 'B', 'a'];
letters.sort();
console.log(letters);
[ 'B', 'a', 'b' ]'B'가 'a'보다 앞에 왔습니다. 글자는 저마다 정해진 번호로 견주는데, 영어 대문자의 번호가 소문자보다 작습니다.
맞는 규칙: 글자 차례에서 영어 대문자는 모두 소문자보다 앞입니다. 'Z'도 'a'보다 앞에 놓입니다.
틀린 생각: 글자 배열도 (a, b) => a - b를 넘겨야 차례대로 놓인다.
3절에서 수 배열은 비교 함수로 바로잡았으니, 글자 배열에도 같은 함수를 넘기면 될 것 같습니다.
const menu = [
'커피',
'녹차',
'라테',
];
const names = menu.toSorted(
(a, b) => a - b,
);
console.log(names);
[ '커피', '녹차', '라테' ]처음 차례 그대로입니다. '커피' - '녹차'처럼 글자끼리 빼면 NaN이고, 비교 함수가 NaN을 돌려주면 0처럼 보아 차례를 바꾸지 않습니다.
맞는 규칙: 글자 배열은 비교 함수 없이 정렬합니다. 한글은 가나다 차례라 menu.toSorted()는 [ '녹차', '라테', '커피' ]입니다.
확인 문제
1. 3절 비교 함수 코드의 가격을 바꾸고, 마지막 줄을 더했습니다. 무엇이 찍힐까요?
const prices = [600, 2500, 800];
const low = prices.toSorted(
(a, b) => a - b,
);
console.log(low);
console.log(prices);
힌트
비교 함수가 있으니 글자가 아니라 수의 크기로 견줍니다. prices는 어느 메서드를 불렀는지 봅니다.
정답
[ 600, 800, 2500 ]
[ 600, 2500, 800 ]a - b로 작은 값부터 놓은 새 배열에 low가 붙고, toSorted()는 prices를 건드리지 않습니다.
흔한 오답은 첫 줄 [ 2500, 600, 800 ]입니다. 비교 함수가 있는데도 글자 차례로 본 것입니다. 둘째 줄을 [ 600, 800, 2500 ]으로 쓴 것은 toSorted()가 처음 배열도 바꾼다고 본 것입니다.
2. 정렬을 두 걸음으로 나눠 씁니다.
(가) 첫 줄에 [ 80, 95, 100 ], 둘째 줄에 [ 80, 100, 95 ]가 찍히도록 빈칸에 메서드 이름을 쓰세요.
const scores = [80, 100, 95];
const low = scores.________(
(a, b) => a - b,
);
console.log(low);
console.log(scores);
힌트
둘째 줄은 처음 차례 그대로입니다. 처음 배열을 건드리지 않는 메서드를 고릅니다.
정답
toSorted입니다.
const scores = [80, 100, 95];
const low = scores.toSorted(
(a, b) => a - b,
);
console.log(low);
console.log(scores);[ 80, 95, 100 ]
[ 80, 100, 95 ]흔한 오답은 sort입니다. 그러면 scores도 제자리에서 정렬되어 둘째 줄도 [ 80, 95, 100 ]이 찍힙니다.
(나) 같은 scores를 큰 점수부터 정렬한 새 배열을 찍는 코드를 쓰세요. [ 100, 95, 80 ]이 찍혀야 합니다.
힌트
예제의 점수 코드처럼 비교 함수에서 a와 b의 자리를 바꿉니다.
정답
const scores = [80, 100, 95];
const high = scores.toSorted(
(a, b) => b - a,
);
console.log(high);[ 100, 95, 80 ]다르게 써도 되는 곳: 새 배열의 이름은 ranked처럼 지어도 됩니다. sort로 써도 같은 값이 찍히지만, 새 배열을 만들지 않고 scores를 바꾸므로 물음과 다릅니다.
흔한 오답은 비교 함수를 뺀 scores.toSorted()입니다. 글자 차례라 [ 100, 80, 95 ]가 찍힙니다.
3. 걸음 수를 날마다 적은 배열입니다. 무엇이 찍힐까요?
const steps = [1200, 9400, 800];
const sorted = steps.sort(
(a, b) => a - b,
);
console.log(steps[0]);
console.log(sorted === steps);
힌트
sort()가 돌려준 값이 새 배열인지, 정렬한 그 배열인지 2절에서 찾습니다.
정답
800
truesort()는 steps가 가리키는 배열을 제자리에서 정렬하고 그 배열을 돌려줍니다. sorted와 steps는 같은 배열을 가리키니 steps[0]은 800이고, === 연산자는 true입니다.
흔한 오답은 1200과 false입니다. sort()가 새 배열을 돌려주고 steps는 그대로라고 본 것입니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
3절의 비교 함수 코드를 main.js에 저장해 돌린 뒤, 3번 줄 (a, b) => a - b,를 지워 봅니다. 다음에는 a - b를 b - a로 바꿔 봅니다. 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다.
저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.