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

JavaScript로 문장 배열에서 단어별 정확한 출현 횟수 세는 방법

문제 상황

JavaScript 개발을 하다 보면 두 개의 문자열 배열에서 특정 단어가 몇 번 등장하는지 정확하게 집계해야 하는 경우가 자주 있습니다. 예를 들어 다음과 같이 하나의 배열에는 이름(단어)들이, 다른 배열에는 문장들이 담겨 있다고 가정해 보겠습니다.

const names = ["jhon", "parker"];
const sentences = [
  "hello jhon",
  "hello parker and parker",
  "jhonny jhonny yes parker"
];

여기서 요구되는 것은 두 개의 문자열 배열을 인자로 받아, 첫 번째 배열(names)에 있는 각 단어가 두 번째 배열(sentences) 전체에서 몇 번 나타나는지를 계산하여 객체 형태로 반환하는 함수를 작성하는 것입니다.

위 예제 데이터의 경우, 최종 출력 결과는 다음과 같은 형태가 됩니다.

const output = {
  "jhon": 1,
  "parker": 3
};

parker가 3으로 카운트된 이유는 두 번째 문장에서 한 번, 세 번째 문장에서 한 번, 그리고 첫 번째와 두 번째 문장에서 각각 한 번씩 등장하기 때문입니다. 반면 jhonny는 jhon과 철자가 비슷하지만 별개의 단어이므로 jhon의 카운트에는 포함되지 않아야 합니다.

해결 방법: 정규표현식 활용

이 문제를 해결하는 가장 깔끔한 방법은 정규표현식(RegExp)을 사용하는 것입니다. 핵심 포인트는 다음과 같습니다.

  • 단어 경계(\b): \b 메타 문자를 사용하면 부분 일치가 아닌 완전한 단어 단위로만 매칭됩니다. 덕분에 'jhon'이 'jhonny' 안에서 잘못 매칭되는 것을 방지할 수 있습니다.
  • OR 연산자(|): 여러 이름을 하나의 패턴으로 결합해 한 번의 검색으로 모든 단어를 찾습니다.
  • g 플래그: 문장 내에서 일치하는 모든 항목을 찾도록 합니다.
  • i 플래그: 대소문자를 구분하지 않고 매칭합니다.

구현 코드

const names = ["jhon", "parker"];
const sentences = [
  "hello jhon",
  "hello parker and parker",
  "jhonny jhonny yes parker"
];

const countAppearances = (names = [], sentences = []) => {
  // 각 이름을 단어 경계가 적용된 정규표현식 패턴으로 변환
  const pattern = new RegExp(
    names.map(name => `\\b${name}\\b`).join('|'),
    'gi'
  );

  const res = {};
  for (const sentence of sentences) {
    for (const match of (sentence.match(pattern) || [])) {
      res[match] = (res[match] || 0) + 1;
    }
  }
  return res;
};

console.log(countAppearances(names, sentences));

코드 동작 원리 살펴보기

  1. names.map(name => `\\b${name}\\b`)는 각 이름을 \\bjhon\\b|\\bparker\\b 형태의 패턴으로 만듭니다.
  2. 'gi' 플래그로 대소문자 구분 없이 모든 일치 항목을 검색합니다.
  3. 각 문장마다 sentence.match(pattern)으로 일치한 단어 목록을 가져오고, 일치 항목이 없으면 빈 배열(|| [])을 사용해 오류를 방지합니다.
  4. res[match] = (res[match] || 0) + 1 코드는 해당 단어가 처음 등장하면 0으로 초기화한 뒤 1을 더하는 방식으로 카운트를 누적합니다.

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다.

{ jhon: 1, parker: 3 }

추가 고려 사항

만약 대소문자를 구분해야 하는 경우라면 정규표현식 생성 시 'gi' 플래그 대신 'g'만 사용하면 됩니다. 또한 결과 객체의 키가 원본 names 배열의 순서나 대소문자를 유지해야 한다면, 매칭된 값을 소문자화한 뒤 원래 키로 다시 매핑하는 후처리 과정을 추가할 수 있습니다. 이처럼 정규표현식과 단어 경계를 활용하면 유사한 단어(jhon vs jhonny)까지 정확하게 구분하는 신뢰성 높은 단어 출현 횟수 집계 기능을 손쉽게 구현할 수 있습니다.