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

자바스크립트(ES6)에서 대소문자와 순서에 관계없이 두 문자열을 비교하는 방법

두 개의 문자열을 인수로 받아, 문자의 등장 순서나 대소문자와 관계없이 두 문자열이 서로 같은 문자들로 구성되어 있는지 판별하는 isEqual() 함수를 작성해야 한다고 가정해 봅시다.

예를 들어 다음과 같습니다.

const first = 'Aavsg';
const second = 'VSAAg';

isEqual(first, second); // true

즉, 두 문자열에 포함된 문자의 종류와 개수만 같다면 순서나 대소문자가 달라도 true를 반환해야 합니다. 이를 구현하는 대표적인 방법 두 가지를 살펴보겠습니다.

방법 1: 배열로 변환해 정렬 후 비교하기

가장 직관적인 접근 방식입니다. 문자열을 배열로 분리한 뒤 Array.prototype.sort() 메서드로 알파벳순 정렬하고, 다시 하나의 문자열로 결합한 다음 두 결과가 완전히 일치하는지 비교합니다.

예제 코드

const first = 'Aavsg';
const second = 'VSAAg';

// 문자열을 자체적으로 정렬할 수 있도록 String.prototype에 sort 메서드 추가
const stringSort = function () {
   return this.split('').sort().join('');
};
String.prototype.sort = stringSort;

const isEqual = (first, second) =>
   first.toLowerCase().sort() === second.toLowerCase().sort();

console.log(isEqual(first, second)); // true

먼저 toLowerCase()로 두 문자열을 모두 소문자로 통일한 뒤, 확장된 sort 메서드가 내부적으로 split → sort → join 과정을 수행해 정렬된 문자열을 만들어 줍니다. 마지막으로 두 정렬 결과가 같은지 === 연산자로 비교합니다.

참고로 실무에서는 표준 객체(String.prototype)를 직접 수정하는 방식은 권장되지 않으므로, 아래처럼 별도의 헬퍼 함수로 분리해 사용하는 것이 더 안전하고 깔끔합니다.

const normalize = str => str.toLowerCase().split('').sort().join('');

const isEqual = (first, second) => normalize(first) === normalize(second);

방법 2: Map(객체)로 문자 빈도수 세기

두 번째 방법은 정렬 없이 두 문자열을 동시에 한 글자씩 순회하면서 각 문자의 빈도수를 객체(Map)에 기록하는 방식입니다. 규칙은 다음과 같습니다.

  • 첫 번째 문자열에서 해당 문자가 나타나면 값에 +1
  • 두 번째 문자열에서 해당 문자가 나타나면 값에 -1

모든 순회가 끝난 후 객체의 모든 값이 0이라면 두 문자열의 문자 구성이 동일한 것이고, 하나라도 0이 아니라면 서로 다르다고 판단합니다.

예제 코드

const first = 'Aavsg';
const second = 'VSAAg';

const isEqual = (first, second) => {
   // 길이가 다르면 바로 false 반환
   if (first.length !== second.length) {
      return false;
   }

   first = first.toLowerCase();
   second = second.toLowerCase();

   const map = {};

   for (let i = 0; i < first.length; i++) {
      // 첫 번째 문자열의 문자는 +1
      map[first[i]] = (map[first[i]] || 0) + 1;

      // 두 번째 문자열의 문자는 -1
      map[second[i]] = (map[second[i]] || 0) - 1;
   }

   // 모든 값이 0이면 두 문자열은 동일
   return Object.values(map).every(val => val === 0);
};

console.log(isEqual(first, second)); // true

함수 초반에 길이를 먼저 비교해 다르면 즉시 false를 반환하므로, 불필요한 순회 연산을 줄일 수 있다는 장점도 있습니다.

두 방법의 성능 비교

정렬 기반 방법(방법 1)의 시간 복잡도는 O(n log n)인 반면, 빈도수 계산 방법(방법 2)은 O(n)으로 문자열이 길어질수록 더 유리합니다. 따라서 짧은 문자열에는 어느 쪽이든 큰 차이가 없지만, 성능이 중요한 환경에서는 Map을 활용한 방법 2를 선택하는 것이 좋습니다.

실행 결과

두 방법 모두 콘솔에는 다음과 같은 결과가 출력됩니다.

true