[TIL, JS] sort () 정렬, 다중 정렬

최현석·2021년 6월 10일
0

6월의 배움

목록 보기
7/13

이 링크를 정리한 내용입니다.
생략한 부분도 있고,
잘못이해한 부분이 있을 수 있으니 원문을 꼭 참고하세요.

정렬 그거 어떻게 하는건데

데이터를 날짜 순으로 정렬을 시켜놨다.
그런데 동일한 날짜 때문에
테이블이 re-rendering이 될 때 마다 동일한 날짜의 row들이 서로 자리를 슥삭슥삭 바꿔대는 이슈가 발생했다.

시간이 없어서
sort의 원리와 속성을 모른채로 감으로다가,
급하게 아래 코드를 짜서 다중 조건 정렬을 했다.

날짜와 로컬 인덱스를 기준으로 다중 정렬

  const dateAndLocalDescending = (a, b) => {
    let dateA = new Date(a["record_date"]).getTime();
    let dateB = new Date(b["record_date"]).getTime();
    let indexA = a["local_index"];
    let indexB = b["local_index"];
    return dateA > dateB ? -1 : indexA > indexB ? 0 : 1;
  };

성공하고도 찜찜해서,

'나중에 이거 꼭 자세히 알아보고 리팩토링 해야지!' 했는데

그게 오늘이다.

배열.Sort(비교함수)

파라미터를 생략하면 각 요소의 문자열 변환에 따라 유니코드 포인트 순서에 의해 정렬이 알아서 된다.

그래서 일반적으로 생각하는 문자열 정렬 순서와는 다를 수 있다.

그리고 주의할 건,

복사본을 return 하는게 아니라

원 배열 자체를 뚝딱 뚝딱 정렬해 버린다.

데이터를 온존하고 싶다면 복사해서 정렬해야겠다.

비교함수

  • (a와 b는 비교되는 두 요소이다.)

비교함수(a,b)의 리턴 값에 따라 정렬된다.

  1. 0보다 작은 경우, a를 b보다 낮은 인덱스로 정렬한다.
  1. 0인 경우, a와 b를 서로 변경하지 않고 넘어간다.
  • ECMAscript가 해당 동작을 보장하지 않아 모든 브라우저가 이처럼 동작하지는 않는다.
  1. 0보다 큰 경우, b를 a보다 낮은 인덱스로 정렬한다.

그리고,

compareFunction(a, b)은 요소 a와 b의 특정 쌍이 두 개의 인수로 주어질 때 항상 동일한 값을 반환해야합니다. 일치하지 않는 결과가 반환되면 정렬 순서는 정의되지 않습니다.

라고 원문에 쓰여있는데 이건 잘 이해가 되지 않는다.

(알고 계신 분은 답글 부탁드립니다.)

따라서 비교함수의 형식은 다음과 같다.

function compare(a, b) {
  if (이 조건에 따르면 a가 b보다 작다.) {
    return -1;
  }
  if (이 조건에 따르면 a가 b보다 크다.) {
    return 1;
  }
  // a가 b와 동일한 경우!
  return 0;
}

문자열 대신 숫자를 비교하기 위해 비교함수는 a에서 b를 뺄 수 있다.

다음 함수는 배열을 오름차순으로 정렬한다.
(infinity 및 NaN 이 포함되어 있지 않은 경우)

function compareNumbers(a, b) {
  return a - b;
}

sort 메소드는 함수식 (및 클로저)와 함께 편리하게 쓸 수 있다.

let numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
  return a - b;
});
console.log(numbers);
// [1, 2, 3, 4, 5]

배열에 객체가 있다면, 해당 속성 중 하나의 값을 기준으로 정렬 할 수 있다.

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic', value: 13 },
  { name: 'Zeros', value: 37 }
];
// value 기준으로 정렬
items.sort(function (a, b) {
  if (a.value > b.value) {
    return 1;
  }
  if (a.value < b.value) {
    return -1;
  }
  // a must be equal to b
  return 0;
});
// name 기준으로 정렬
items.sort(function(a, b) {
  var nameA = a.name.toUpperCase(); // ignore upper and lowercase
  var nameB = b.name.toUpperCase(); // ignore upper and lowercase
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }
  // 이름이 같을 경우
  return 0;
});

sort의 비교함수는 매 요소 마다 하노이의 탑을 한다.

배열내의 요소마다 여러 번 호출되어,

높은 오버헤드가 발생할 수 있다.

비교함수가 복잡해지고, 정렬할 요소가 많아질 경우

임시배열을 하나 만들어,

map메소드를 이용해 실제로 정렬할 요소만 추려넣어 정렬 시킨 뒤,

이 리턴값을 이용해 실제 정렬을 하는

map을 사용한 정렬을 고려해보자.

map을 사용한 정렬은 다음과 같다.

// 소트 할 배열
let list = ['Delta', 'alpha', 'CHARLIE', 'bravo'];
// 임시 배열은 위치 및 정렬 값이있는 객체를 보유합니다.
let mapped = list.map(function(el, i) {
  return { index: i, value: el.toLowerCase() };
})
// 축소 치를 포함한 매핑 된 배열의 정렬
mapped.sort(function(a, b) {
  return +(a.value > b.value) || +(a.value === b.value) - 1;
});
// 결과 순서를 위한 컨테이너
let result = mapped.map(function(el){
  return list[el.index];
});

그래서,

이제 정렬 리팩토링을 하려고 하는데,

아무래도 데이터가 많다보니

sort 보다는 map을 사용한 방식으로 리팩토링 해야겠다.

profile
노력과 성장을 기록합니다.

0개의 댓글