HyunJun 기술 블로그

sort() 본문

JavaScript

sort()

공부 좋아 2023. 7. 4. 09:20
728x90
반응형

1. sort() 이해

sort 메서드는, 배열 안의 값을 일정한 알고리즘으로 정렬할 수 있는 메서드로 Array.prototype에 정의되어 있는 프로토타입 메서드이다. 기본적으로 숫자로 된 배열이나, 문자열로 된 배열 등을 정렬할 수 있다. sort() 메서드는 메서드 사용 시, 원본 인스턴스의 값을 정렬된 값으로 변경한다.

const numArray = [1, 10, 100, 2, 20, 200, 123, 456, 789];
const strArray = ["가", "가가", "가가가", "나", "나나", "나나나", "가나다", "라마바", "사아자"];

console.log("before:", numArray, strArray);
numArray.sort();
strArray.sort();
console.log("after:", numArray, strArray);

 

결과를 확인해 보니, 예상했던 1, 2, 10의 오름차순 정렬이 아닌, 1, 10, 100 순서로 오름차순 정렬이 된 것을 확인할 수 있다. 문자열도 같은 로직으로 오름차순 정렬이 된 것을 확인할 수 있다.

 

이렇게 동작하는 이유는, sort()의 매개변수로 값을 넘기지 않는다면 단순히 전체적인 값의 크기를 따져서 순서를 생각해 정렬을 해주는 것이 아닌, 맨 앞자리 1개의 숫자(문자열)의 유니코드를 우선 기준으로  정렬한다. 한번 직접 유니코드 값이 어떻게 나오는지 확인해 보자.

 

아래의 코드는 숫자 배열의 숫자 값을 하나하나씩 확인하며 아스키코드 번호를 확인하기 위해 문자열로 변환하고 변환한 문자열을 아스키코드로 확인하는 코드이다.

  const numArray = [1, 10, 100, 2, 20, 200, 123, 456, 789];

  for (let i = 0; i < numArray.length; i++) {
    console.log(numArray[i].toString().charCodeAt(0));
  }

배열 안의 숫자를 맨 앞자리만 봤을 때, 1(49), 1(49), 1(49), 2(50), 2(50), 2(50), 1(49), 4(52), 7(55)가 되고, 유니코드 안의 아스키코드 값 정렬에 따라 1, 10, 100, 123, 2, 20, 200, 456, 789가 되는 것이다.

 

문자열도 위의 숫자처럼 유니코드로 확인해 보면

  const strArray = ["가", "가가", "가가가", "나", "나나", "나나나", "가나다", "라마바", "사아자"];

  for (let i = 0; i < numArray.length; i++) {
    console.log(strArray[i].charCodeAt(0));
  }

동일하게 맨 앞의 문자만 기준으로 유니코드로 정렬하기 때문에 같은 결과가 나온 것이다.

 

2. sort() 활용

하지만 정렬에는 여러 가지 정렬이 있고, 사용자가 간단한 로직으로 여러 가지 정렬을 구현할 수 있게 미리 정의되어 있다. 정렬에 관한 정의를 바꿀 때에는 아래의 형태처럼 사용해서 정렬 기준을 변경할 수 있다. 즉 콜백 함수로 comparefunction을 넣어주면 된다.

array.sort(comparefunction);

 

3. comparefunction

  • sort의 콜백 함수를 작성할 때에 생각할 점은 일단 매개 변수 2개를 받는다.
  • 이 매개 변수는 a, b라는 네이밍을 많이 한다.
    • 하지만 이는 헷갈릴 여지가 있어 처음 학습할 때에는 next, prev 등으로 네이밍 하는 것을 추천한다.

 

1) 숫자의 내림차순 정렬

숫자 기준 맨 앞자리만을 확인하는 게 아닌 전체적인 값의 크기에 따라 정렬하고 싶다면, 어떻게 해야 할까? 여기서는 단순 사용법 만이 아닌 원리를 이해해 보려고 한다. comparefunction을 익명 함수로써 콜백 함수로 전달했다.

const numArray = [1, 10, 100, 2, 20, 200, 3, 30, 300];

numArray.sort((next, prev) => {
	console.log("next: ", next, "/ prev:", prev);
});

 

아래와 같은 결과가 나왔다 이게 무엇을 뜻할까? 일단 매개 변수의 네이밍으로 a, b보다 next, prev를 추천한 이유가 보인다. sort 알고리즘 첫 시작 시 next(a)는 1번 인덱스를 가리키고, prev(b)는 0번 인덱스를 가리킨다.

기본 원리는 알았고 이제, 모든 비교에 대해 리턴 값을 -1로 줘보자.

  numArray.sort((next, prev) => {
    console.log("next: ", next, "/ prev:", prev);

    return -1;
  });

  console.log(numArray);

 

 

순서가 역전되는 것을 확인할 수 있다. 이것으로 알 수 있는 것은 -1은 next를 prev의 앞으로 이동한다는 것이다.

 

즉 아래에서처럼 리턴으로 음수 값 (-1)을 받으면 prev 앞에 next를 위치시킨다.

  // 1 10 100 2 20 200
  // 10 1 100 2 20 200
  // 100 10 1 2 20 200

  // 2 100 10 1 20 200
  // 20 2 100 10 1 200
  // 200 20 2 100 10 1

 

찍어놓은 next 값과, prev 값 그리고 위의 변화되는 값들을 확인해 보면 된다.

 

하지만 지금 같은 상황에서 결과가 아래처럼 주어진다면

const numArray = [3, 30, 300, 2, 20, 200, 1, 10, 100];

무조건 역전 시키기 때문에 100으로 시작하여 3으로 끝날 것이다. 

 

그렇다면 내림차순으로 정렬할 시 이러한 조건이 성립되면 되지 않을까? 위의 배열을 기준으로 3(prev) - 30(next)을 해서 음수가 나오면 3 하고 30을 바꾸기, 만약 300(prev) - 2(next)를 했을 때 양수가 나오면 300을 2앞에다가 두기, 이것을 아래처럼 작성하면 구현된다.

  const numArray = [3, 30, 300, 2, 20, 200, 1, 10, 100];

  numArray.sort((next, prev) => {
    return prev - next;
  });

  console.log(numArray);

이제는 정확히 값의 크기에 따른 내림차순 정렬이 구현됐다.

 

즉 여기서의 포인트는 아래와 같은 규칙이 적용된다. 하지만 내부적으로 비교하는 알고리즘은 브라우저마다 차이가 있을 수 있다.

  • next는 1번 인덱스, prev는 0번 인덱스부터 시작하여, 알고리즘에 의해 두 개의 값을 비교하게 된다.
  • 반환 값이 0보다 작다면, next가 prev보다 앞에 위치하게 된다.
  • 반환 값이 0이라면, next와 prev의 순서를 바꾸지 않는다.
  • 반환 값이 0보다 크다면, prev가 next보다 앞에 위치하게 된다.

 

2) 숫자의 오름차순 정렬

  const numArray = [3, 30, 300, 2, 20, 200, 1, 10, 100];

  numArray.sort((next, prev) => {
    return next - prev;
  });

  console.log(numArray);

 

3) 문자열의 오름차순

문자열도 원리를 잘 생각해 보면 똑같다. 하지만 이는 유니코드로 비교하기에 <, > 등으로 비교한다.

  const strArray = ["가", "가가", "가나다", "A", "aa", "B", "bb", "나", "다", "라라"];

  strArray.sort((next, prev) => {
    if (next > prev) return 1;
    if (next < prev) return -1;
    // if (next === prev) return  // 생략 가능
  });

  console.log(strArray);

문자열은 숫자처럼 그 자체로 크기라는 게 없으므로, 맨 앞의 문자에 따라 정렬이 된다. 이때 유니코드 순번 상, 영어 대문자, 영어 소문자, 한글 순서로 알고리즘이 구현된다.

4) 문자열의 내림차순

즉 유니코드 순번 상이라고 했으니, 오름차순 일 때는 영어가 먼저지만, 내림차순 일 때는 한글이 먼저이다.

  const strArray = ["가", "가가", "가나다", "A", "aa", "B", "bb", "나", "다", "라라"];

  strArray.sort((next, prev) => {
    if (next < prev) return 1;
    if (next > prev) return -1;
    // if (next === prev) return  // 생략 가능
  });

  console.log(strArray);

브라우저 엔진마다 우리가 작성한 리턴 값에 따른 결과값은 같지만 내부적으로 정렬을 하는 알고리즘은 다르다.

 

5) 전체 예제.

  // 숫자 내림차순
  function compareFunction1(next, prev) {
    return prev - next;
  }

  // 숫자 오름차순
  function compareFunction2(next, prev) {
    return next - prev;
  }

  // 문자열의 오름차순
  function compareFunction3(next, prev) {
    if (next > prev) return 1;
    if (next < prev) return -1;
    if (next === prev) return 0; // 생략 가능
  }

  // 문자열의 내림차순
  function compareFunction4(next, prev) {
    if (next < prev) return 1;
    if (next > prev) return -1;
    if (next === prev) return 0; // 생략 가능
  }

  let arr1 = [1, 10, 2, 20, 123];
  let arr2 = [1, 10, 2, 20, 123];

  let arr3 = ["a", "A", "BB", "cC", "abc", "Abc", "가", "가가가", "나나", "나다"];
  let arr4 = ["a", "A", "BB", "cC", "abc", "Abc", "가", "가가가", "나나", "나다"];

  console.log(arr1.sort(compareFunction1));
  console.log(arr2.sort(compareFunction2));
  console.log(arr3.sort(compareFunction3));
  console.log(arr4.sort(compareFunction4));

728x90
반응형
Comments