8 분 소요


1. sort 개요

1.1 sort의 개념

배열의 요소를 정렬(sorting)하는 데에 사용된다.


1.2 sort 기본 문법

// []가 있다는 것은 'optional(선택사항)'임을 의미
array.sort([compareFunction]);


1.3 sort의 기본적 특징

① 정렬이 된 배열을 반환한다.
② 배열 그 자체가 변환된다.


⚠️ 하지만 아래와 같이 배열 그 자체가 변환된다는 것은 원본 배열도 그대로 유지할 수 없다는 문제점이 있다.

참조형 데이터는 같은 주솟값을 사용하기 때문에 let sortedArray = originalArry;는 안되는 것이다.!

let originalArray = [3, 1, 4, 1, 5, 9];

// 원본 배열을 복사한 후 정렬하려는 시도
let sortedArray = originalArray;
sortedArray.sort();

console.log(sortedArray); // [ 1, 1, 3, 4, 5, 9 ]
console.log(originalArray); // [ 1, 1, 3, 4, 5, 9 ]
console.log(sortedArray === originalArray); // true → 같은 주솟값 가리킴


메모리 관점에서의 주소가 다른 것을 이용하여 이 같은 문제를 해결할 수 있다.
아래와 같은 solution이 존재한다.

① slice() 사용
originalArray.slice() === [...originalArray]

let originalArray = [3, 1, 4, 1, 5, 9];

// 원본 배열의 복사본을 만들고 그것을 정렬
let sortedArray = originalArray.slice().sort();

console.log(sortedArray); // [ 1, 1, 3, 4, 5, 9 ]
console.log(originalArray); // [ 3, 1, 4, 1, 5, 9 ]
console.log(sortedArray === originalArray); // false


② spread operator 사용
spread operator로 펼치고 배열로 묶어준다.

let originalArray = [3, 1, 4, 1, 5, 9];

// 원본 배열의 복사본을 만들고 그것을 정렬
let sortedArray = [...originalArray].sort();

console.log(sortedArray); // [ 1, 1, 3, 4, 5, 9 ]
console.log(originalArray); // [ 3, 1, 4, 1, 5, 9 ]
console.log(sortedArray === originalArray); // false



2. sort 의 기본 원리

2.1 sort 비교 함수의 작동 방식

먼저 비교 함수에 대해 알아보자

위에서 살펴보았던 sort 문법을 사용하는 방법은 아래와 같다.

array.sort([compareFunction]);

즉, 비교 함수란 [compareFunction] 자리에 들어가는 함수를 말한다.

array.sort(비교 함수);


비교 함수가 어떻게 작동이 되길래 sort가 정렬이 되는 것일까?

sort 메서드에는 정렬 순서를 정의하는 비교 함수를 전달할 수 있다.(optional)
만일 이 함수를 생략한다면 배열 요소들이 문자열로 ‘자동’ 변환되어 각 문자의 유니코드 코드 포인트 값에 따라 정렬된다.

유니코드 코드 포인트란?
유니코드 표준에서 각 문자에 할당된 고유한 숫자 값이다. 즉, 모든 문자가 코드의 조합으로 이루어져 있다는 것이다. ((ex) 영문 대문자 A는 “U+0041”, 한글 ‘가’는 “U+AC00”으로 표현)


따라서 compareFunction이 제공되지 않으면 요소를 문자열로 변환하고 유니 코드 코드 포인트 순서로 문자열을 비교하여 정렬이 되기 때문에 예상치 못한 값을 얻을 수 있다는 문제점이 있다.

arr = [1, 2, 9, 80];
arr.sort((a, b) => a - b);

console.log(arr); // [ 1, 2, 9, 80 ]


sort()sort((a, b) ⇒ a - b)의 차이점

비교함수를 입력하지 않는 경우, 자동으로 유니코드 문자로 비교하게 된다는 차이점이 있다.


즉, 왠만해서는 sort 함수의 비교 함수를 비어놓지 말아야한다! ⚠️

  • 비교 함수는 두 개의 인자를 받고, 반환 값에 따라 순서가 결정이 된다.
정렬 방법 숫자 문자열
오름차순 numbers.sort((a, b) => a - b); strings.sort((a, b) => a.localeCompare(b));
내림차순 numbers.sort((a, b) => b - a); strings.sort((a, b) => b.localeCompare(a));
arr = [1, 2, 9, 80];
arr.sort((a, b) => a - b);

console.log(arr); // [ 1, 2, 9, 80 ]

즉, 위 코드에서는 인자로 받은 a, b를 통해 배열 안의 값들 중 2개를 계속 비교하는 과정을 거친다. (비교 후 정렬)


arr.sort((a, b) => a - b); 가 정렬되는 과정을 그림으로 나타내면 다음과 같다! (오름차순)

음수일 때만 자리를 바꾼다.


※ 내림차순 정렬은 위 로직과 반대이다.


정말 위의 그림처럼 정렬되는지 테스트해보자!

const testArr = [2, 1, 9, 80];
testArr.sort(function (a, b) {
  console.log("---테스트 시작---");

  console.log("a", a);
  console.log("b", b);
  console.log("a-b", a - b);

  console.log(a - b >= 0 ? "안바꿈" : "바꿈");

  console.log("---종료---");
});



3. sort로 정렬하기

3.1 문자열 정렬하기

기본적으로 sort()는 문자열로 변환 후 유니코드 순으로 배열 요소를 정렬

const fruits = ["banana", "cherry", "apple"];
fruits.sort();
console.log(fruits); // ['apple', 'banana', 'cherry']


3.2 숫자 정렬하기

  • 그러나, 숫자 배열을 정렬할 때, 숫자의 크기가 아닌 문자열의 순서대로 정렬
  • 숫자의 크기에 따라 배열을 정렬하려면 비교 함수를 sort 메서드에 전달해야 함
const numbers = [10, 5, 100, 1, 2000];
numbers.sort();
console.log(numbers); // [1, 10, 100, 2000, 5]
const numbers = [10, 5, 100, 1];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 5, 10, 100]


3.3 객체 정렬하기

const items = [
  { name: "Banana", price: 1 },
  { name: "Cherry", price: 3 },
  { name: "Apple", price: 2 },
];
items.sort((a, b) => a.price - b.price); // price를 기준으로 정렬
console.log(items); // [ { name: 'Banana', price: 1 },  { name: 'Apple', price: 2 },  { name: 'Cherry', price: 3 } ]



4. 다양한 상황에서의 sort 응용

4.1 객체로 이루어진 배열의 정렬

객체가 아래와 같을 때,

var items = [
  { name: "Edward", age: 21 },
  { name: "Sharpe", age: 37 },
  { name: "And", age: 45 },
  { name: "The", age: -12 },
  { name: "Magnetic", age: 13 },
  { name: "Zeros", age: 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;
});

console.log(items);


② 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;
});

console.log(items);


age별로 내림차순 정렬

// value 기준으로 정렬
items.sort(function (a, b) {
  return b.age - a.age;
});

console.log(items);


4.2 조건부 sort

  • 각 객체가 agename 속성을 가진다.
  • 이 배열을 age 속성에 따라 오름차순으로 정렬하되, age가 같은 경우에는 name 속성으로 사전순으로 정렬한다.
let people = [
  { name: "John", age: 25 },
  { name: "Jane", age: 20 },
  { name: "Mary", age: 25 },
  { name: "Mark", age: 30 },
];

people.sort((a, b) => {
  if (a.age === b.age) {
    // 나이가 같으면 이름으로 정렬
    return a.name.localeCompare(b.name);
  }
  // 나이로 정렬
  return a.age - b.age;
});

console.log(people);



5. React에서 sort 사용하기

5.1 기본 사용 방법

기본 정렬 함수

const [sortOrder, setSortOrder] = useState("asc");

// 아이템을 정렬하는 함수
const sortItems = (order) => {
  // items 배열을 복사하여 정렬 시작 (원본 배열을 변경하지 않기 위해 spread operator로 복사)
  const sortedItems = [...items].sort((a, b) => {
    // 오름차순 정렬
    if (order === "asc") {
      return new Date(a.deadline) - new Date(b.deadline);
    }
    // 내림차순 정렬
    return new Date(b.deadline) - new Date(a.deadline);
  });
  setItems(sortedItems); // 정렬된 아이템으로 리스트 상태 업데이트
};

정렬 옵션 선택 컴포넌트

선택된 정렬 순서에 따라 setTodos를 호출하여 정렬된 리스트를 업데이트한다.

import { useState } from "react";

const TodoSort = ({ setTodos }) => {
  const [sortOrder, setSortOrder] = useState("asc");

  const onChangeSortOrder = (e) => {
    const newSortOrder = e.target.value; // 사용자가 선택한 값
    setSortOrder(newSortOrder); // 상태를 업데이트

    setTodos((prev) =>
      [...prev].sort((a, b) => {
        const dataA = new Date(a.deadline);
        const dateB = new Date(b.deadline);

        // 오름차순 정렬
        if (newSortOrder === "asc") {
          return dataA - dateB;
        }
        //내림차순 정렬
        else {
          return dateB - dataA;
        }
      })
    );
  };

  return (
    <select value={sortOrder} onChange={onChangeSortOrder}>
      <option value="asc">오름차순</option>
      <option value="desc">내림차순</option>
    </select>
  );
};

export default TodoSort;


5.2 유틸리티 파일로 분리

유틸리티 파일 생성

먼저, 정렬 로직을 유틸리티 함수로 분리한다.

// src/utils/sortUtils.js
export const sortByDate = (items, order = "desc") => {
  return [...items].sort((a, b) => {
    const dateA = new Date(a.deadline);
    const dateB = new Date(b.deadline);
    if (order === "asc") {
      return dateA - dateB;
    }
    return dateB - dateA;
  });
};


컴포넌트에서 사용

  • 이제 컴포넌트에서 정렬 유틸리티 함수를 사용하여 정렬 로직을 적용하면 된다.
  • useEffect를 사용하는 경우, 의존성 배열에 sortOrder을 넣어줘서 sortOrder가 변경될 때 게시글을 다시 정렬하게 해줘야한다!
import { useState } from "react";
import { sortByDate } from "/utils/sortUtils"; // 정렬 유틸리티 함수 import

const TodoSort = ({ setTodos }) => {
  const [sortOrder, setSortOrder] = useState("asc");

  const onChangeSortOrder = (e) => {
    const newSortOrder = e.target.value; // 사용자가 선택한 값
    setSortOrder(newSortOrder); // 상태를 업데이트

    setTodos((prev) => sortByDate(prev, newSortOrder)); // 유틸리티 함수 사용
  };

  return (
    <select value={sortOrder} onChange={onChangeSortOrder}>
      <option value="asc">오름차순</option>
      <option value="desc">내림차순</option>
    </select>
  );
};

export default TodoSort;


실제 사용 예시

import { fetchPosts } from "api/posts";
import dayjs from "dayjs";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import { sortByDate } from "utils/sortUtils";

const PostListPage = () => {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [sortOrder, setSortOrder] = useState("desc");
  const navigate = useNavigate();

  // HTML 태그 제거 및 엔터티 변환 함수
  const convertHtmlEntities = (htmlString) => {
    const parser = new DOMParser();
    const doc = parser.parseFromString(htmlString, "text/html");
    return doc.body.textContent || "";
  };

  // 정렬 순서 변경 핸들러
  const onChangeSortOrder = (e) => {
    const newSortOrder = e.target.value;
    setSortOrder(newSortOrder);
  };

  useEffect(() => {
    const loadPosts = async () => {
      try {
        const response = await fetchPosts();
        const sortedPosts = sortByDate(response.data, sortOrder); // 게시글 정렬
        setPosts(sortedPosts);
      } catch (error) {
        console.error(error);
        alert(error.response.data);
      } finally {
        setLoading(false);
      }
    };
    loadPosts();
  }, [sortOrder]); // sortOrder가 변경될 때 게시글을 다시 정렬

  if (loading) {
    return <p>로딩 중...</p>;
  }

  return (
    <div>
      <select value={sortOrder} onChange={onChangeSortOrder}>
        <option value="asc">오름차순</option>
        <option value="desc">내림차순</option>
      </select>

      {posts.length > 0 ? (
        <ul>
          {posts.map((post) => {
            const textContent = convertHtmlEntities(post.content); // HTML 엔터티 변환
            return (
              <StPostItem
                key={post.id}
                onClick={() => navigate(`/posts/${post.id}`)}
              >
                <h3>제목: {post.title}</h3>
                <p>내용: {textContent}</p> {/* HTML 엔터티가 변환된 내용 표시 */}
                <p>작성일: {dayjs(post.createdAt).format("YYYY/MM/DD")}</p>
                <p>작성자: {post.author.nickname}</p>
                <p>댓글 수: {post.comments.length}</p>
              </StPostItem>
            );
          })}
        </ul>
      ) : (
        <p>등록된 글이 없습니다.</p>
      )}
    </div>
  );
};

export default PostListPage;

const StPostItem = styled.li`
  padding: 1rem;
  border: 1px solid black;
  cursor: pointer;

  p {
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
`;



6. 문제

1. 질문: JavaScript의 sort 함수는 기본적으로 어떤 기준으로 정렬하나요?

답변: 기본적으로 sort 함수는 배열의 요소들을 문자열로 변환한 후, 유니코드 코드 포인트에 따라 정렬합니다.


2. 질문: sort 함수에 비교 함수를 제공하지 않으면 어떻게 되나요?

답변: 비교 함수를 제공하지 않으면, sort는 요소들을 문자열로 취급하여 정렬합니다. 숫자 배열의 경우 예상치 못한 결과가 나올 수 있습니다.


3. 질문: sort 함수의 비교 함수는 어떤 매개변수를 받나요?

답변: 비교 함수는 두 매개변수를 받습니다. 이 두 매개변수는 배열의 요소를 대표하며, 이들을 비교하여 정렬 순서를 결정합니다. arr.sort(function(어떤매개변수){ return });


4. 질문: sort 함수에서 반환되는 값의 의미는 무엇인가요?

답변: 반환 값이 음수이면 첫 번째 요소가 두 번째 요소보다 앞에 위치하게 되고, 양수이면 그 반대가 됩니다. 0이 반환되면 두 요소의 순서는 변경되지 않습니다.


5. 질문: sort 함수로 숫자 배열을 오름차순으로 정렬하려면 어떻게 해야 하나요?

답변: 숫자 배열을 오름차순으로 정렬하려면, sort((a, b) => a - b)와 같은 비교 함수를 사용해야 합니다.


6. 질문: sort 함수로 객체 배열을 특정 속성 기준으로 정렬하려면 어떻게 해야 하나요?

답변: 객체의 특정 속성을 기준으로 정렬하려면, 그 속성을 참조하는 비교 함수를 작성해야 합니다. 예: array.sort((a, b) => a.age - b.age).


7. 질문: sort 함수는 배열을 어떻게 변경하나요?

답변: sort 함수는 원본 배열을 직접 변경합니다. 새로운 배열을 반환하지 않으며, 정렬된 원본 배열을 반환합니다.


8. 질문: sort 함수를 사용할 때 주의할 점은 무엇인가요?

답변: sort 함수는 원본 배열을 변경하므로, 원본 데이터를 유지해야 할 경우 복사본을 사용하는 것이 좋습니다.


9. 질문: sort 함수로 내림차순 정렬은 어떻게 하나요?

답변: 내림차순으로 정렬하려면, sort((a, b) => b - a)와 같은 비교 함수를 사용합니다.


10. 질문: sort 함수를 이용한 알파벳 정렬은 어떻게 하나요?

답변: 문자열 배열을 알파벳 순으로 정렬하려면, sort() 또는 대소문자 구분 없이 정렬하려면 sort((a, b) => a.localeCompare(b))를 사용합니다.


카테고리:

업데이트:

댓글남기기