Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript .sort() 메서드의 함정 피하기 — 원본 배열은 그대로, 정렬된 새 배열 만드는 방법

JavaScript의 Array.prototype.sort() 함수에는 중요한 특징이 있습니다. 바로 제자리(in-place) 정렬 알고리즘이라는 점입니다. 즉, 정렬 대상 배열의 복사본을 새로 만들지 않고 기존 배열 자체를 정렬하기 때문에 추가 메모리를 소비하지 않아 효율적이고 성능이 뛰어납니다.

하지만 이러한 특성이 때로는 곤란한 상황을 초래합니다.

문제 상황

예시를 통해 살펴보겠습니다. 문자열로 구성된 names 배열이 있다고 가정해 봅시다. 우리는 이 배열의 원래 순서는 그대로 유지하면서, 동일한 요소를 담되 알파벳순으로 정렬된 별도의 배열을 하나 더 만들고 싶습니다.

그래서 다음과 같이 코드를 작성했습니다.

const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj'];
let sortedNames = names;
sortedNames = sortedNames.sort();
console.log(names);
console.log(sortedNames);

그런데 JavaScript에서 배열 역시 객체이며, 객체는 값(value)이 아닌 참조(reference)로 복사된다는 사실 때문에 한쪽 배열을 정렬하면 두 배열이 모두 정렬되어 버립니다. 물론 우리가 의도한 결과가 아니죠.

해결 방법

1. 새 배열 초기화 시 slice() 사용하기

const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj'];
let sortedNames = names.slice();
sortedNames = sortedNames.sort();
console.log(names);
console.log(sortedNames);

slice() 메서드는 얕은 복사(shallow copy)를 수행하여 호출된 배열의 복사본을 새 배열로 반환합니다. 인자를 전달하지 않으면 처음부터 끝까지 전체 요소를 복사합니다.

다만 이 방법은 새 배열을 초기화하는 과정이 포함되어 다소 비효율적이며, 문자열·숫자 리터럴로 구성된 배열에만 효과적이라는 한계가 있습니다.

2. JSON.stringify() / JSON.parse() 사용하기

const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj'];
let sortedNames = JSON.parse(JSON.stringify(names));
sortedNames = sortedNames.sort();
console.log(names);
console.log(sortedNames);

배열을 JSON 문자열로 변환한 뒤 다시 배열로 되돌리는 과정은, 컴파일러가 참조 방식으로 복사하지 못하도록 강제하는 효과가 있습니다. 덕분에 첫 번째 방법보다 조금 더 효율적일 뿐만 아니라, 객체로 구성된 배열에서도 안정적으로 동작합니다.

두 방법 모두 콘솔에서 동일한 출력 결과를 보여줍니다.

출력 결과

[ 'Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj' ]
[ 'Anshul', 'Dheeraj', 'Mukesh', 'Rakesh', 'Ram' ]

참고: 최신 문법 toSorted() 활용하기

ES2023부터 도입된 toSorted() 메서드를 사용하면 원본 배열은 그대로 둔 채 정렬된 새 배열을 반환받을 수 있습니다. 별도의 복사 과정 없이 문제를 깔끔하게 해결할 수 있는 현대적인 대안이니, 지원 환경이 허용된다면 이 방법을 우선 고려해 보세요.

const names = ['Rakesh', 'Mukesh', 'Ram', 'Anshul', 'Dheeraj'];
const sortedNames = names.toSorted(); // 원본은 변경되지 않음