Skip to content

Commit

Permalink
feat: 숫자를 순 우리말 수사로 변환하거나 수 관형사로 변환하는 함수를 추가 (#201)
Browse files Browse the repository at this point in the history
* feat: 상수 추가

* feat: 수사 추가

* test: susa에 대한 테스트케이스 추가

* feat: 테스트케이스를 통과하도록  수정

* feat: 에 대한 문서 추가

* feat: 에서 를 export 하도록 수정

* refactor: 변할 수 있는 변수 선언인 let보다는 최대한 const를 사용

* feat: 정수가 아닌 경우에 대한 예외처리를 추가하고 분기를 탈 확률이 가장 낮은 조건을 뒤로 배치

* test: 정수가 아닌 값에 대한 테스트케이스 추가

* refactor: let이 사용되던 부분을 동일하게 제거

* Create itchy-nails-bake.md

---------

Co-authored-by: 박찬혁 <[email protected]>
  • Loading branch information
BO-LIKE-CHICKEN and okinawaa committed Aug 1, 2024
1 parent 506d0fc commit 56db7f0
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/itchy-nails-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"es-hangul": patch
---

feat: 숫자를 순 우리말 수사로 변환하거나 수 관형사로 변환하는 함수를 추가
33 changes: 33 additions & 0 deletions docs/src/pages/docs/api/susa.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: susa
---

# susa

Convert numbers to native Korean numeral words or numeral determiners. The given number is valid when it is greater than 0 and less than or equal to 100.

```typescript
function susa(
// Number to convert
num: number,
// Whether to use numeral determiners
classifier?: boolean
): string;
```

## Examples

```typescript
susa(1); // '하나'
susa(2); // '둘'
susa(11); // '열하나'
susa(21); // '스물하나'
susa(99); // '아흔아홉'
susa(100); // '백'

susa(1, true); // '한'
susa(2, true); // '두'
susa(11, true); // '열한'
susa(20, true); // '스무'
susa(21, true); // '스물한'
```
33 changes: 33 additions & 0 deletions docs/src/pages/docs/api/susa.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: susa
---

# susa

숫자를 순 우리말 [수사](https://ko.dict.naver.com/#/entry/koko/d0ce2b674cae4b44b9028f648dd458b0)로 변환하거나 [수 관형사](https://ko.dict.naver.com/#/entry/koko/c513782b82554ff499c80ec616c5b611)로 변환합니다. 주어진 숫자가 0보다 크고 100 이하일 때 유효합니다.

```typescript
function susa(
// 변환할 숫자
num: number,
// 수 관형사를 사용할지 여부
classifier?: boolean
): string;
```

## Examples

```typescript
susa(1); // '하나'
susa(2); // '둘'
susa(11); // '열하나'
susa(21); // '스물하나'
susa(99); // '아흔아홉'
susa(100); // '백'

susa(1, true); // '한'
susa(2, true); // '두'
susa(11, true); // '열한'
susa(20, true); // '스무'
susa(21, true); // '스물한'
```
30 changes: 30 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,33 @@ export const QWERTY_KEYBOARD_MAP = {
m: 'ㅡ',
M: 'ㅡ',
} as const;

export const SUSA_MAP = {
1: '하나',
2: '둘',
3: '셋',
4: '넷',
5: '다섯',
6: '여섯',
7: '일곱',
8: '여덟',
9: '아홉',
10: '열',
20: '스물',
30: '서른',
40: '마흔',
50: '쉰',
60: '예순',
70: '일흔',
80: '여든',
90: '아흔',
100: '백',
} as const;

export const SUSA_CLASSIFIER_MAP = {
1: '한',
2: '두',
3: '세',
4: '네',
20: '스무',
} as const;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export {
} from './utils';
export { extractHangul } from './extractHangul';
export { acronymizeHangul } from './acronymizeHangul';
export { susa } from './susa';
42 changes: 42 additions & 0 deletions src/susa.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { susa } from './susa';

describe('susa', () => {
const validNumbers = [
{ num: 1, word: '하나', classifier: '한' },
{ num: 2, word: '둘', classifier: '두' },
{ num: 3, word: '셋', classifier: '세' },
{ num: 4, word: '넷', classifier: '네' },
{ num: 5, word: '다섯', classifier: '다섯' },
{ num: 6, word: '여섯', classifier: '여섯' },
{ num: 7, word: '일곱', classifier: '일곱' },
{ num: 8, word: '여덟', classifier: '여덟' },
{ num: 9, word: '아홉', classifier: '아홉' },
{ num: 10, word: '열', classifier: '열' },
{ num: 11, word: '열하나', classifier: '열한' },
{ num: 12, word: '열둘', classifier: '열두' },
{ num: 20, word: '스물', classifier: '스무' },
{ num: 21, word: '스물하나', classifier: '스물한' },
{ num: 30, word: '서른', classifier: '서른' },
{ num: 99, word: '아흔아홉', classifier: '아흔아홉' },
{ num: 100, word: '백', classifier: '백' },
];

const invalidNumbers = [0, -1, 101, 1.1, -1.1, Infinity, -Infinity, NaN];

validNumbers.forEach(({ num, word, classifier }) => {
it(`${num} - 순 우리말 수사로 바꿔 반환해야 한다.`, () => {
expect(susa(num, false)).toBe(word);
});

it(`${num} - 순 우리말 수 관형사가 있다면 수 관형사로 없다면 수사로 반환해야 한다.`, () => {
expect(susa(num, true)).toBe(classifier);
});
});

invalidNumbers.forEach(num => {
it(`유효하지 않은 숫자 ${num}에 대해 오류를 발생시켜야 한다.`, () => {
expect(() => susa(num, false)).toThrow('지원하지 않는 숫자입니다.');
expect(() => susa(num, true)).toThrow('지원하지 않는 숫자입니다.');
});
});
});
57 changes: 57 additions & 0 deletions src/susa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { SUSA_MAP, SUSA_CLASSIFIER_MAP } from './constants';
import { hasProperty } from './utils';

export function susa(num: number, classifier?: boolean): string {
validateNumber(num);
return classifier ? getClassifierWord(num) : getNumberWord(num);
}

function getClassifierWord(num: number): string {
if (num === 20) {
return SUSA_CLASSIFIER_MAP[num];
}

const tens = Math.floor(num / 10) * 10;
const ones = num % 10;

const tensWord = hasProperty(SUSA_MAP, tens) ? SUSA_MAP[tens] : '';

if (ones === 0) {
return tensWord;
}

if (hasProperty(SUSA_CLASSIFIER_MAP, ones)) {
const onesWord = SUSA_CLASSIFIER_MAP[ones];

return `${tensWord}${onesWord}`;
}

if (hasProperty(SUSA_MAP, ones)) {
const onesWord = SUSA_MAP[ones];

return `${tensWord}${onesWord}`;
}

// `susa`에서` `validateNumber` 하기 때문에 도달할 수 없는 분기입니다. 타입 추론을 위해 에러를 던져줍니다.
throw new Error('지원하지 않는 숫자입니다.');
}

function validateNumber(num: number): void {
if (Number.isNaN(num) || num <= 0 || num > 100 || !Number.isInteger(num) || !Number.isFinite(num)) {
throw new Error('지원하지 않는 숫자입니다.');
}
}

function getNumberWord(num: number): string {
if (num === 100) {
return SUSA_MAP[100];
}

const tens = Math.floor(num / 10) * 10;
const ones = num % 10;

const tensWord = hasProperty(SUSA_MAP, tens) ? SUSA_MAP[tens] : '';
const onesWord = hasProperty(SUSA_MAP, ones) ? SUSA_MAP[ones] : '';

return `${tensWord}${onesWord}`;
}

0 comments on commit 56db7f0

Please sign in to comment.