Post

JavaScript 문자열 비교와 지혜롭게 sort()활용하기

JavaScript 문자열 비교와 지혜롭게 sort()활용하기

image.png

요즘 너무나 알찬 시간을 보내고 있는데, 이것저것 다 공부하고 싶은 욕심에 정리하는 시간이 줄어든 것 같다 🥲 BUT 언제나 목적이 수단과 전치되어서는 안된다. 형식적으로 보여주기 위한 정리는 정리의 본질인 “학습과 공유”를 흐린다. 퀄리티가 떨어져 좋은 글을 공유하지 못하고, 정리하며 복습되는 효과도 떨어지기 때문이다.

따라서, “꼭 잊지 말아야 할 내용”이거나 몇 번 반복되어 내 부족한 개념으로 주목되었을 때 내재화를 위해 정리하자.

해당 관점에서 문자열 비교와 sort는 … 우선순위 토픽에 해당한다 ㅎ.


자바스크립트의 문자열 비교

자바스크립트의 String을 비교하면 어떤 일이 벌어질까? 먼저 문자열 안에도 다양한 형태가 포함될 수 있다. 알파벳, 특수기호, 숫자 등.

(1) 알파벳 vs 알파벳

사전 순서대로 비교하게 된다. 사전순이란, 앞에서부터 각 알파벳의 사전 순서를 비교하는 것이다. 사전순으로 “뒤”에 있는 것이 더 큰 값과 같이 평가된다.

1
"apple" < "bag"// true"apple" < "Bag"// false"bag" > "beach"// false"true" > "false"// true

한편, 동일하게 “apple”과 “bag”를 비교했음에도 다른 결과가 나오는 이유는 무엇일까? “apple”과 “Bag”를 비교할 때는 B가 대문자여서 더 앞의 값으로 평가된다. 아스키 코드상 대문자가 소문자보다 앞에 나오기 때문이다.

(2) 숫자 vs 숫자

숫자일 때도 마찬가지다. 우리가 상식적으로 알고 있는 정수, 실수의 비교가 아니라 각 자리의 숫자를 비교한다. 숫자 234가 숫자34보다 크지만, 맨 앞자리인 3이 2보다 크기때문에 문자열 비교에서는 “34”가 더 크게 평가된다.

1
"234" > "34"// false

(3) 특수기호가 포함되었을 때?

위의 모든 예시들과 같은 원리가 적용된다. 앞에서부터 각 자리를 비교하되, “!”가 “}”보다 아스키코드 상 앞에 위치하기 때문에 더 작은값으로 평가된다.

1
"a!b" > "a}b"// false"a" > "!"// true

배열의 정렬

자바스크립트에서 배열을 정렬할 때 대부분의 경우 sort()를 사용한다. sort는 나와 애증의 관계다. 희노애락과 … 최근의 아픈기억이 함께한다 ㅎ^^ㅎ

특히 코딩테스트 문제, 알고리즘 풀이를 하다보면 다양한 조건과 상황에 맞게 정렬을 하는 순간이 온다. 앞으로는 슬픈 기억을 만들지 않기 위해 구멍났던 부분을 정리해보자.

🧚‍♀️ sort()의 기본 원리

sort는 기본적으로 배열에 적용하는 메서드다. 매개변수로 compareFunction을 받는데, 이 compareFunction의 기본적인 동작을 아는 것이 좋다.

compareFunction

sort의 매개변수로 아무것도 넘겨주지 않으면, 사전순 정렬이 된다. 그 이유는 compareFunction은 기본적으로 배열을 순회하며 (a > b) - (a < b)의 연산 결과에 따라 a와 b의 순서를 결정하기 때문이다.

우리가 앞에서 보았듯 문자열 대소 비교는 사전순서에 따른다.

a = “bag” b = “apple”이라고 가정해보자. 아래와 같이 a>b는 true, a<b는 false이다.

1
// 1. a > b"bag" > "apple"// true// 2. a < b"bag" < "apple"// false

둘 사이의 뺄셈을 하면 어떻게 될까? 불리언값이 각각 number로 자동으로 형변환을 하여 연산된다. true는 1, false는 0으로.

이에 따라 a > b (true) - a < b (false)는 1 - 0, 즉 1이 된다. compareFunction은 각 뺄셈의 연산결과에 따라 다음과 같이 판단한다.

  • 연산결과가 음수면 a → b 순서라고 판단
  • 0을 반환하면 a == b라고 판단
  • 연산결과가 양수면 b → a 순서라고 판단

해당 예시에서는 양수기 때문에 “apple” 다음 “bag”의 순서가 된다.

🍊 활용1

이러한 compareFunction의 원리에 따라, 기본적으로 숫자를 오름차순/내림차순 정렬을 실행할 때 다음과 같이 쓴다.

1
2
arr.sort((a,b) => a-b);// 오름차순
arr.sort((a,b) => b-a);// 내림차순

🍊 활용2

양수 / 0 / 음수의 반환값에 따라 순서를 결정하는 compareFunction의 원리를 따른다면 형태는 얼마든지 바꿔도 된다.

1
2
3
4
5
arr.sort((a,b) => {
	if(a < b) return -1;
	else if(a > b) return 1;
	else return 0;
});// 오름차순 정렬과 동일하다.

아래 두 줄의 코드도 동일하게 사전순 정렬로 동작한다.

1
2
arr.sort()
arr.sort((a,b) => (a>b) - (a<b));

🧚‍♀️ sort를 200% 활용하기

한 가지 기준에 대해 정렬하다보면 분명히 동일한 순위로 평가되는 대상들이 생긴다. 이에 대한 처리를 크게 2가지 방법으로 할 수 있다.

(1) compareFunction에서 같을 때의 조건을 따로 처리

위의 오름차순 정렬을 했던 compareFunction을 가져와서 생각해보자. else문은 a와 b가 같을 때에 해당한다. 이때 return 0을 하는 것이 아니라 더 세부적인 처리 로직을 추가하면 된다.

1
2
3
4
5
6
7
8
9
10
arr.sort((a,b) => {
	if(a < b) return -1;
	else if(a > b) return 1;
	else {
		... 여기에 직접 로직 작성 ...
		if(추가조건) return 1;
		else return -1;

	};
});

(2) compareFunction의 리턴값을 다양하게 정의하기

아래처럼, 여러 개의 조건을 간단하게 추가할 수 있다. 선행 조건에서 동등하게 평가되는 경우에 뒤의 조건에서 다시 평가된다.

1
data.sort((a,b) => 첫번째 조건 || 추가조건1 || 추가조건2);

유의할 점은! compareFunction의 기본원리처럼 부호에 따라 순서를 판단할 수 있게 해주어야 한다.

가령, 문자열을 비교할 때 단순히 “a” > “b” 형태로 조건을 넘겨주어서는 안된다. boolean형태이기 때문이다.

→ 이럴 땐 localeCompare를 활용할 수 있다.

참조문자열.localeCompare(비교문자열)은 참조 문자열이 정렬 순으로 지정된 문자열 앞 혹은 뒤에 오는지, 동일한 문자열인지 나타내는 ‘수치’를 반환한다. 한마다로, 사전순 비교를 하여, 참조문자열이 비교문자열보다 앞이면 음수 / 동일하면 0 / 뒤면 양수다.

This post is licensed under CC BY 4.0 by the author.