[JS] sort()로 배열 정렬하기
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
- 각 객체가
age
와name
속성을 가진다. - 이 배열을
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))
를 사용합니다.
댓글남기기