Skip to content

Commit

Permalink
fix: 코드 효율 개선 (#124)
Browse files Browse the repository at this point in the history
* fix: 모든 글자의 자소를 나누지 않고 마지막 글자만 계산합니다.

* fix: /\s/g to /\s+/g 일괄치환 효율 증가 bench: https://jsfiddle.net/crucify/g74b5d1h/

* fix: NFD 로 변환하여 자소계산 과정 생략하고 초성 ㄱ-ㅎ 만 남기게 수정

* fix: 자소계산을 생략하고 받침이 있는지만 검사합니다.

* fix: 유니코드 상수화 가독성 향상

* fix: 기존함수 받침문제 테스트 추가 (이 브랜치에서 해결됨)

* Create sixty-clouds-repeat.md

---------

Co-authored-by: 박찬혁 <pgg6713@gmail.com>
  • Loading branch information
crucifyer and okinawaa authored Jun 29, 2024
1 parent db94449 commit 0f38431
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/sixty-clouds-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"es-hangul": patch
---

fix: 코드 효율 개선
4 changes: 2 additions & 2 deletions src/chosungIncludes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { disassembleHangulToGroups } from './disassemble';
import { canBeChosung, getChosung } from './utils';

export function chosungIncludes(x: string, y: string) {
const trimmedY = y.replace(/\s/g, '');
const trimmedY = y.replace(/\s+/g, '');

if (!isOnlyChosung(trimmedY)) {
return false;
}

const chosungX = getChosung(x).replace(/\s/g, '');
const chosungX = getChosung(x).replace(/\s+/g, '');
const chosungY = trimmedY;

return chosungX.includes(chosungY);
Expand Down
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ export const COMPLETE_HANGUL_END_CHARCODE = '힣'.charCodeAt(0);
export const NUMBER_OF_JONGSUNG = 28;
export const NUMBER_OF_JUNGSUNG = 21;

const _JASO_HANGUL_NFD = [...'각힣'.normalize('NFD')].map(char => char.charCodeAt(0)); // NFC 에 정의되지 않은 문자는 포함하지 않음
export const JASO_HANGUL_NFD = {
START_CHOSEONG: _JASO_HANGUL_NFD[0], // ㄱ
START_JUNGSEONG: _JASO_HANGUL_NFD[1], // ㅏ
START_JONGSEONG: _JASO_HANGUL_NFD[2], // ㄱ
END_CHOSEONG: _JASO_HANGUL_NFD[3], // ㅎ
END_JUNGSEONG: _JASO_HANGUL_NFD[4], // ㅣ
END_JONGSEONG: _JASO_HANGUL_NFD[5], // ㅎ
}

/**
* ㄱ -> 'ㄱ'
* ㄳ -> 'ㄱㅅ' 으로 나눈다.
Expand Down
1 change: 1 addition & 0 deletions src/removeLastHangulCharacter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { removeLastHangulCharacter } from './removeLastHangulCharacter';
describe('removeLastHangulCharacter', () => {
it('마지막 문자가 겹받침인 경우 홑받침으로 바꾼다.', () => {
expect(removeLastHangulCharacter('안녕하세요 값')).toBe('안녕하세요 갑');
expect(removeLastHangulCharacter('안녕하세요 값이')).toBe('안녕하세요 값ㅇ');
});
it('마지막 문자가 초성과 중성의 조합으로 끝날 경우 초성만 남긴다.', () => {
expect(removeLastHangulCharacter('프론트엔드')).toBe('프론트엔ㄷ');
Expand Down
20 changes: 4 additions & 16 deletions src/removeLastHangulCharacter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,13 @@ import { excludeLastElement } from './_internal';
* removeLastHangulCharacter('신세계') // 신세ㄱ
*/
export function removeLastHangulCharacter(words: string) {
const disassembledGroups = disassembleHangulToGroups(words);
const lastCharacter = disassembledGroups[disassembledGroups.length - 1];

const lastCharacter = words[words.length - 1];
if (lastCharacter == null) {
return '';
}

const withoutLastCharacter = disassembledGroups
.filter(v => v !== lastCharacter)
.map(([first, middle, last]) => {
if (middle != null) {
return combineHangulCharacter(first, middle, last);
}

return first;
});

const [[first, middle, last]] = excludeLastElement(lastCharacter);
const disassembleLastCharacter = disassembleHangulToGroups(lastCharacter);
const [[first, middle, last]] = excludeLastElement(disassembleLastCharacter[0]);
const result = middle != null ? combineHangulCharacter(first, middle, last) : first;

return [...withoutLastCharacter, result].join('');
return [words.substring(0, words.length - 1), result].join('');
}
43 changes: 33 additions & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import {
COMPLETE_HANGUL_START_CHARCODE,
COMPLETE_HANGUL_END_CHARCODE,
HANGUL_CHARACTERS_BY_FIRST_INDEX,
HANGUL_CHARACTERS_BY_LAST_INDEX,
HANGUL_CHARACTERS_BY_MIDDLE_INDEX,
NUMBER_OF_JONGSUNG,
JASO_HANGUL_NFD,
} from './constants';
import { disassembleHangul, disassembleHangulToGroups } from './disassemble';
import { disassembleCompleteHangulCharacter } from './disassembleCompleteHangulCharacter';
import { disassembleHangulToGroups } from './disassemble';

const EXTRACT_CHOSEONG_REGEX = new RegExp(
`[^\\u${JASO_HANGUL_NFD.START_CHOSEONG.toString(16)}-\\u${JASO_HANGUL_NFD.END_CHOSEONG.toString(16)}ㄱ-ㅎ\\s]+`,
'ug'
);
const CHOOSE_NFD_CHOSEONG_REGEX = new RegExp(
`[\\u${JASO_HANGUL_NFD.START_CHOSEONG.toString(16)}-\\u${JASO_HANGUL_NFD.END_CHOSEONG.toString(16)}]`,
'g'
);

/**
* @name hasBatchim
Expand All @@ -26,9 +38,14 @@ export function hasBatchim(str: string) {
if (lastChar == null) {
return false;
}
const charCode = lastChar.charCodeAt(0);
const isCompleteHangul = COMPLETE_HANGUL_START_CHARCODE <= charCode && charCode <= COMPLETE_HANGUL_END_CHARCODE;

const disassembled = disassembleCompleteHangulCharacter(lastChar);
return disassembled != null && disassembled.last !== '';
if (!isCompleteHangul) {
return false;
}

return (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSUNG > 0;
}

/**
Expand All @@ -49,12 +66,18 @@ export function hasBatchim(str: string) {
export function hasSingleBatchim(str: string) {
const lastChar = str[str.length - 1];

if (lastChar == null || hasBatchim(lastChar) === false) {
if (lastChar == null) {
return false;
}
const charCode = lastChar.charCodeAt(0);
const isCompleteHangul = COMPLETE_HANGUL_START_CHARCODE <= charCode && charCode <= COMPLETE_HANGUL_END_CHARCODE;

const disassembled = disassembleHangul(lastChar);
return disassembled.length === 3;
if (!isCompleteHangul) {
return false;
}

const batchimCode = (charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSUNG;
return HANGUL_CHARACTERS_BY_LAST_INDEX[batchimCode].length === 1;
}

/**
Expand All @@ -73,9 +96,9 @@ export function hasSingleBatchim(str: string) {
* getChosung('띄어 쓰기') // 'ㄸㅇ ㅆㄱ'
*/
export function getChosung(word: string) {
return disassembleHangulToGroups(word).reduce((chosung, [consonant]) => {
return `${chosung}${consonant}`;
}, '');
return word.normalize('NFD')
.replace(EXTRACT_CHOSEONG_REGEX, '') // NFD ㄱ-ㅎ, NFC ㄱ-ㅎ 외 문자 삭제
.replace(CHOOSE_NFD_CHOSEONG_REGEX, $0 => HANGUL_CHARACTERS_BY_FIRST_INDEX[$0.charCodeAt(0) - 0x1100]); // NFD to NFC
}

/**
Expand Down

0 comments on commit 0f38431

Please sign in to comment.