Language/Javascript

Javascript - sort 함수를 이용한 데이터 정렬

둉이 2022. 5. 10. 22:47

 

자바스크립트에서 배열을 정렬하기 위해서는 보통 sort() 메소드를 사용한다.

 

 

배열.sort([비교 함수])

비교 함수는 ((이전 값, 현재 값) => { 정렬 가중치 함수 }) 형태로 이루어짐
정렬 가중치 함수는 특정 조건에 따라서 1, 0, -1 처럼 정렬의 기준이 되는 값을 리턴하거나 혹은 숫자 배열의 경우에는 가중치 자체가 되는 값을 리턴한다.

 

sort() 메소드는 원본 배열을 변경하는 함수이며, 동시에 변경된 배열을 리턴하기 때문에 특정 변수에 sort된 배열 값을 저장할 수 있다.

 

그리고 비교 함수를 인자로 전달할 수 있다. 비교 함수는 다음과 같은 형태를 갖는다.

// 조건에 따라 0보다 큰 수(1), 0, 0보다 작은 수(-1) 중 하나의 값을 반환한다.
(prev, cur) => {
  // 해당 조건을 만족할 경우, prev -> cur 순으로 정렬
  if (prev > cur) return 1;
  
  // 해당 조건을 만족할 경우, cur -> prev 순으로 정렬
  if (prev < cur) return 1;
  
  // 해당 조건을 만족할 경우, 이 조건 외에 다른 조건을 적용하여 정렬
  if (prev === cur) { ... };
}

 

아무 인자도 넘기지 않으면 기본적으로 유니코드 값(ASCII 값)에 의해 정렬된다.

 

숫자와 알파벳 대/소문자의 ASCII 값은 다음과 같다.

 

- 숫자

0(48) ~ 10(57)

 

- 대문자

A(65) ~ Z(90)

 

- 소문자

a(97) ~ z(122)

 

 

유니코드 값 정렬은 문자열 배열의 오름차순 정렬과 동일하다고 보면 된다.

 

만일 숫자 배열에 아무 인자도 넘기지 않고 sort() 함수를 적용하는 경우에는 문자열로 간주하여 정렬된다.

 

n차원 배열의 경우에는 해당 배열 요소의 첫 번째 요소를 기준으로 유니코드 오름차순 정렬이 이루어진다.

 

객체 배열의 경우에는 비교 함수를 전달하지 않는 경우에는 정렬이 불가능하니 참고하자.

 

 

그럼 비교 함수는 어떤 걸 전달하면 될까?

 

아래 예시를 통해 각 자료형에 따라 어떤 방식으로 사용하면 되고, 어떻게 비교 함수를 전달하면 되는지 알아보자.

 

 

문자열 배열 정렬

- 오름차순/내림차순 정렬

문자열로 이루어진 배열을 오름차순으로 정렬할 때는 기본적으로 아무 인자도 넘겨주지 않고 호출하면 된다.

 

내림차순으로 정렬하고 싶다면 아래 예시처럼 비교 함수를 전달하면 된다.

 

비교 함수는 0보다 큰 수(보통 1을 주로 사용) 혹은 0보다 작은 수(보통 -1을 주로 사용), 0 세 가지 경우를 반환한다.

 

반환하는 숫자가 곧 정렬에 사용되는 가중치라고 생각하면 된다.

 

만약, 0보다 큰 수를 반환하게 되면 prev 요소가 cur 요소보다 먼저 오도록 정렬된다.

 

반대로 0보다 작은 수를 반환하게 되면 cur 요소가 prev 요소보다 먼저 오도록 정렬된다.

 

0을 반환하게 되면 해당 조건일 때는 순서를 변경하지 않고 다른 조건을 적용하여 정렬한다.

(표준 자바스크립트에서는 이러한 동작을 보장하지 않기 때문에 굳이 명시해 주지 않아도 된다.)

 

아래는 문자열 오름차순/내림차순 예시 코드이다.

 

prev > cur인 경우에 1을 반환하게 되면 prev가 cur보다 먼저 정렬되므로 오름차순 정렬이 되고, -1을 반환하게 되면 cur가 prev보다 먼저 정렬되므로 내림차순 정렬이 된다.

const arr = ['conan', 'apples', 'apple', 'zone', 'park', 'astronaut'];

arr.sort();  // 오름차순, ['apple', 'apples', 'astronaut', 'conan', 'park', 'zone']

arr.sort((prev, cur) => {  // 오름차순, 위와 동일하게 동작
    if (prev > cur) return 1;
    if (prev < cur) return -1;
    if (prev === cur) return 0;  // 생략 가능
});

arr.sort((prev, cur) => {  // 내림차순, ['zone', 'park', 'conan', 'astronaut', 'apples', 'apple']
    if (prev < cur) return 1;
    if (prev > cur) return -1;
    if (prev === cur) return 0;  // 생략 가능
});

 

- 대/소문자 구분 없이 정렬

sort() 함수는 유니코드 기준으로 문자열을 정렬하므로, 기본적으로 대문자가 소문자보다 앞에 오도록 정렬이 이루어진다.

 

대소문자 구분 없이 정렬을 하기 위해서는 비교 함수 내에서 prev, cur 값을 대문자 혹은 소문자로 변환한 후 조건식을 적용하면 된다.

const arr = ['apples', 'Apples', 'apple', 'Apple', 'Astronaut', 'astronaut'];

arr.sort();  // 대문자 -> 소문자 순으로 정렬, ['Apple', 'Apples', 'Astronaut', 'apple', 'apples', 'astronaut']

arr.sort((prev, cur) => {  // 오름차순, ['apple', 'Apple', 'apples', 'Apples', 'Astronaut', 'astronaut']
    const [upperPrev, upperCur] = [prev.toUpperCase(), cur.toUpperCase()]
    if (upperPrev > upperCur) return 1;
    if (upperPrev < upperCur) return -1;
    if (upperPrev === upperCur) return 0;  // 생략 가능
});

arr.sort((prev, cur) => {  // 내림차순, ['Astronaut', 'astronaut', 'apples', 'Apples', 'apple', 'Apple']
    const [upperPrev, upperCur] = [prev.toUpperCase(), cur.toUpperCase()]
    if (upperPrev < upperCur) return 1;
    if (upperPrev > upperCur) return -1;
    if (upperPrev === upperCur) return 0;  // 생략 가능
});

 

- 문자열 길이순 정렬

문자열 길이 오름차순/내림차순 정렬도 아래와 같이 .length 속성을 이용하여 적용할 수 있다.

const arr = ['apples', 'app', 'a', 'Apple', 'Astronaut', 'astronautss'];

arr.sort((prev, cur) => {  // 길이 오름차순, ['a', 'app', 'Apple', 'apples', 'Astronaut', 'astronautss']
    if (prev.length > cur.length) return 1;
    if (prev.length < cur.length) return -1;
});

arr.sort((prev, cur) => {  // 길이 내림차순, ['astronautss', 'Astronaut', 'apples', 'Apple', 'app', 'a']
    if (prev.length < cur.length) return 1;
    if (prev.length > cur.length) return -1;
});

 

 

숫자 배열 정렬

number 요소를 갖는 배열의 경우에는 일반 문자열 배열처럼 아무 비교 함수도 전달하지 않고 사용하게 되면 정상적으로 정렬이 이루어지지 않는다.

 

예를 들어, [222, 1, 10000, 456, 8888] 배열에 인자가 없는 sort() 함수를 적용하게 되면 [1, 10000, 222, 456, 8888] 순으로 정렬된다.

 

따라서, 숫자 배열을 정렬할 때는 꼭 비교 함수를 함께 전달해야 오름차순/내림차순 정렬이 가능하다.

 

비교 함수는 문자열 정렬과 마찬가지로 1, 0, -1을 반환하는 함수를 전달해도 되지만, 다음과 같이 두 값의 차를 반환하는 함수를 인자로 전달할 수 있다.

 

오름차순의 경우에는 a - b, 내림차순의 경우에는 b - a를 반환하는 간단한 함수로도 정렬이 가능하다.

const arr = [222, 1, 10000, 456, 8888];

arr.sort();  // 유니코드 순, [1, 10000, 222, 456, 8888]
arr.sort((a, b) => a - b);  // 오름차순, [1, 222, 456, 8888, 10000]
arr.sort((a, b) => b - a);  // 내림차순, [10000, 8888, 456, 222, 1]

// 물론 이 방법도 가능하다, 오름차순
arr.sort((prev, cur) => {  // 오름차순
    if (prev > cur) return 1;
    if (prev < cur) return -1;
});

 

객체 배열 정렬

- 하나의 조건 적용

객체 배열 정렬의 경우에는 정렬하고자 하는 프로퍼티를 조건으로 하는 비교 함수를 인자로 전달하면 된다.

 

위와 마찬가지로 prev.프로퍼티명 > cur.프로퍼티명 조건에서 1을 반환하면 해당 프로퍼티 기준 오름차순 정렬, -1을 반환하면 내림차순으로 정렬한다.

const arr = [
  { name: 'Edward', age: 21, grade: 2 },
  { name: 'Sharpe', age: 37, grade: 4 },
  { name: 'And', age: 45, grade: 1 },
  { name: 'The', age: -12, grade: 6 },
  { name: 'Magnetic', age: 13, grade: 5 },
  { name: 'Zeros', age: 37, grade: 1 }
];

arr.sort((prev, cur) => {  // name 기준 오름차순 정렬
  if (prev.name > cur.name) return 1;
  if (prev.name < cur.name) return -1;
});

arr.sort((prev, cur) => {  // age 기준 내림차순 정렬
  if (prev.age < cur.age) return 1;
  if (prev.age > cur.age) return -1;
});

 

- 둘 이상의 조건 적용

두 가지 이상의 속성을 이용하여 정렬하고자 한다면, 다음과 같이 차례대로 조건을 명시하면 된다.

 

아래 예시는 age 관련 조건을 먼저 명시하고, grade 관련 조건을 다음에 명시했기 때문에 age를 기준으로 1차 정렬을 한 후, grade를 기준으로 2차 정렬이 이루어지게 된다.

const arr = [
  { name: 'Edward', age: 21, grade: 2 },
  { name: 'Sharpe', age: 37, grade: 4 },
  { name: 'And', age: 45, grade: 1 },
  { name: 'The', age: 13, grade: 6 },
  { name: 'Magnetic', age: 13, grade: 5 },
  { name: 'Zeros', age: 37, grade: 1 }
];

arr.sort((prev, cur) => {  // age를 기준으로 내림차순 -> grade를 기준으로 오름차순 정렬
  if (prev.age < cur.age) return 1;
  if (prev.age > cur.age) return -1;
  if (prev.grade > cur.grade) return 1;
  if (prev.grade < cur.grade) return -1;
});

 

 

2차원 이상 배열 정렬

- 하나의 기준 적용

다른 정렬 방식과 동일하며, 정렬 기준으로 삼고자 하는 배열 요소(인덱스로 구분)를 기준으로 비교 함수를 작성하여 넘겨주면 된다.

const arr = [
  [10, 2], [7, 8], [3, 3], [-1, 3], [99, 99], [-10, 10]
];

arr.sort((prev, cur) => {  // 2번째 배열 요소를 기준으로 오름차순
  if (prev[1] > cur[1]) return 1;
  if (prev[1] < cur[1]) return -1;
});

const arr = [
  [2, 9, 2], [-1, -2, -3], [0, 3, 3], [12, -1, 6], [9, 10, 11], [-1, -1, -1], [1, 0, -5]
];

arr.sort((prev, cur) => {  // 3번째 배열 요소를 기준으로 내림차순
  if (prev[2] < cur[2]) return 1;
  if (prev[2] > cur[2]) return -1;
});

 

- 둘 이상의 기준 적용

객체 배열 정렬과 마찬가지로 정렬하고자 하는 순서으로 조건을 명시하면 된다.

const arr = [
  [3, 2], [7, 8], [3, 3], [-10, 3], [99, 99], [-10, 10]
];

arr.sort((prev, cur) => {  // 1번째 요소 오름차순 -> 2번째 요소 오름차순
  if (prev[0] > cur[0]) return 1;
  if (prev[0] < cur[0]) return -1;
  if (prev[1] > cur[1]) return 1;
  if (prev[1] < cur[1]) return -1;
});

const arr = [
  [2, 9, 2], [-1, -2, -3], [0, 3, 3], [12, -1, 6], [9, 10, 11], [-1, -1, -1], [1, 0, -5]
];

arr.sort((prev, cur) => {  // 1번째 요소 내림차순 -> 3번째 요소 내림차순
  if (prev[0] < cur[0]) return 1;
  if (prev[0] > cur[0]) return -1;
  if (prev[2] < cur[2]) return 1;
  if (prev[2] > cur[2]) return -1;
});

 

 

sort() 함수에 넘겨야 할 비교 함수가 복잡해질 경우에는 map()을 사용하여 배열 형태를 변경한 후에 정렬하는 방법도 있다.

 

 

map()을 이용한 정렬

배열에 map() 고차함수를 적용하면 인덱스 혹은 기타 다른 속성을 포함하는 객체 배열 형태로 변환할 수 있다.

 

변환된 배열에 sort() 함수를 적용하고 사용할 비교 함수를 넘겨주면 된다.

 

아래는 문자열 배열인 arr를 map()을 사용하여 대/소문자 구분 없이 오름차순 정렬하는 예시 코드이다.

const arr = ['Delta', 'alpha', 'CHARLIE', 'bravo'];

const mapped = arr.map((e, i) => {
  return { index: i, value: e.toLowerCase() };
});

mapped.sort((a, b) => {
  return +(a.value > b.value) || +(a.value === b.value) - 1;
});

const result = mapped.map(({ index }) => arr[index]);

 

 

그 외 주어진 배열의 요소 순서를 뒤집는 reverse() 함수도 있다.

 

배열.reverse()

해당 배열의 순서를 뒤집는 함수

 

이 함수도 sort()와 마찬가지로 원본 배열을 변경하는 함수이므로 사용에 주의해야 한다.

 

 

참고 자료

 

Array.prototype.sort() - JavaScript | MDN

sort() 메서드는 배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환합니다. 정렬은 stable sort가 아닐 수 있습니다. 기본 정렬 순서는 문자열의 유니코드 코드 포인트를 따릅니다.

developer.mozilla.org