Skip to content

Commit 5244525

Browse files
wet6123okinawaa
andauthored
feat: 숫자를 순 우리말 서수사로 변환하는 함수 추가 (#307)
* feat: 서수사 기능 구현 * feat: 서수사 테스트 추가 * refactor: 숫자 유효성 검증 로직 개선 Co-authored-by: 박찬혁 <[email protected]> * docs: 서수사 함수 주석 보완 * docs: 서수사 함수 문서 추가 * fix: update seosusa function can handle more than 100 numbers * update: seosusa test code * update: seosusa documents * Create modern-trains-change.md --------- Co-authored-by: 박찬혁 <[email protected]>
1 parent 102f1e2 commit 5244525

File tree

7 files changed

+235
-0
lines changed

7 files changed

+235
-0
lines changed

.changeset/modern-trains-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"es-hangul": patch
3+
---
4+
5+
feat: 숫자를 순 우리말 서수사로 변환하는 함수 추가
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: seosusa
3+
---
4+
5+
import { Sandpack } from '@/components/Sandpack';
6+
7+
# susa
8+
9+
Convert numbers to Korean ordinal words.
10+
Integers from 1 to 99 are converted to native Korean ordinal words.
11+
Integers greater than 100 are converted to Sino-Korean ordinal words.
12+
13+
```typescript
14+
function seosusa(
15+
// Number to convert
16+
num: number
17+
): string;
18+
```
19+
20+
## Examples
21+
22+
```typescript
23+
seosusa(1); // '첫째'
24+
seosusa(2); // '둘째'
25+
seosusa(3); // '셋째'
26+
seosusa(10); // '열째'
27+
seosusa(11); // '열한째'
28+
seosusa(12); // '열두째'
29+
seosusa(13); // '열셋째'
30+
seosusa(20); // '스무째'
31+
seosusa(21); // '스물한째'
32+
seosusa(99); // '아흔아홉째'
33+
seosusa(100); // '백째'
34+
```
35+
36+
## Demo
37+
38+
<br />
39+
40+
<Sandpack>
41+
42+
```ts index.ts
43+
import { seosusa } from 'es-hangul';
44+
45+
console.log(seosusa(1));
46+
```
47+
48+
</Sandpack>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
title: seosusa
3+
---
4+
5+
import { Sandpack } from '@/components/Sandpack';
6+
7+
# seosusa
8+
9+
숫자를 한글 서수사로 변환합니다.
10+
1부터 99까지의 정수는 순우리말 서수사 문자열로 변환합니다.
11+
100 이상의 정수는 한자어 서수사 문자열로 변환합니다.
12+
13+
```typescript
14+
function seosusa(
15+
// 변환할 숫자
16+
num: number
17+
): string;
18+
```
19+
20+
## Examples
21+
22+
```typescript
23+
seosusa(1); // '첫째'
24+
seosusa(2); // '둘째'
25+
seosusa(3); // '셋째'
26+
seosusa(10); // '열째'
27+
seosusa(11); // '열한째'
28+
seosusa(12); // '열두째'
29+
seosusa(13); // '열셋째'
30+
seosusa(20); // '스무째'
31+
seosusa(21); // '스물한째'
32+
seosusa(99); // '아흔아홉째'
33+
seosusa(100); // '백째'
34+
```
35+
36+
## 사용해보기
37+
38+
<br />
39+
40+
<Sandpack>
41+
42+
```ts index.ts
43+
import { seosusa } from 'es-hangul';
44+
45+
console.log(seosusa(1));
46+
```
47+
48+
</Sandpack>

src/seosusa/constants.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export const SEOSUSA_MAP = {
2+
1: '한',
3+
2: '두',
4+
3: '셋',
5+
4: '넷',
6+
5: '다섯',
7+
6: '여섯',
8+
7: '일곱',
9+
8: '여덟',
10+
9: '아홉',
11+
10: '열',
12+
20: '스물',
13+
30: '서른',
14+
40: '마흔',
15+
50: '쉰',
16+
60: '예순',
17+
70: '일흔',
18+
80: '여든',
19+
90: '아흔',
20+
100: '백',
21+
};
22+
23+
export const SEOSUSA_SPECIAL_CASE_MAP = {
24+
1: '첫',
25+
2: '둘',
26+
20: '스무',
27+
};

src/seosusa/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './seosusa';

src/seosusa/seosusa.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { seosusa } from './seosusa';
2+
3+
describe('seosusa', () => {
4+
const validNumbers = [
5+
{ num: 1, word: '첫째' },
6+
{ num: 2, word: '둘째' },
7+
{ num: 3, word: '셋째' }, // '셋째'가 표준어이고 '세째'는 비표준어이다.(표준어 사정 원칙 제6항)
8+
{ num: 4, word: '넷째' }, // '넷째'가 표준어이고 '네째'는 비표준어이다.(표준어 사정 원칙 제6항)
9+
{ num: 5, word: '다섯째' },
10+
{ num: 6, word: '여섯째' },
11+
{ num: 7, word: '일곱째' },
12+
{ num: 8, word: '여덟째' },
13+
{ num: 9, word: '아홉째' },
14+
{ num: 10, word: '열째' },
15+
{ num: 11, word: '열한째' },
16+
{ num: 12, word: '열두째' }, // '둘째'는 십 단위 이상의 서수사에 쓰일 때에 '두째'로 한다.(표준어 사정 원칙 제6항)
17+
{ num: 13, word: '열셋째' },
18+
{ num: 14, word: '열넷째' },
19+
{ num: 15, word: '열다섯째' },
20+
{ num: 20, word: '스무째' },
21+
{ num: 21, word: '스물한째' },
22+
{ num: 22, word: '스물두째' },
23+
{ num: 30, word: '서른째' },
24+
{ num: 40, word: '마흔째' },
25+
{ num: 90, word: '아흔째' },
26+
{ num: 99, word: '아흔아홉째' },
27+
{ num: 100, word: '백째' },
28+
{ num: 101, word: '백일째' },
29+
];
30+
31+
const invalidNumbers = [0, -1, 1.1, -1.1, Infinity, -Infinity, NaN];
32+
33+
validNumbers.forEach(({ num, word }) => {
34+
it(`${num} - 순 우리말 서수사로 변환한다.`, () => {
35+
expect(seosusa(num)).toBe(word);
36+
});
37+
});
38+
39+
invalidNumbers.forEach(num => {
40+
it(`${num} - 유효하지 않은 숫자에 대해 오류를 발생시켜야 한다.`, () => {
41+
expect(() => seosusa(num)).toThrow('유효하지 않은 입력입니다. 1이상의 정수만 지원합니다.');
42+
});
43+
});
44+
});

src/seosusa/seosusa.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { numberToHangul } from '@/numberToHangul';
2+
import { hasProperty } from '../_internal';
3+
import { SEOSUSA_MAP, SEOSUSA_SPECIAL_CASE_MAP } from './constants';
4+
5+
/**
6+
* 숫자를 한글 서수사로 변환합니다.
7+
*
8+
* @remarks
9+
* - **서수사**는 순서를 나타내는 단어입니다.
10+
* - 1부터 99까지의 정수는 순우리말 서수사 문자열로 변환합니다.
11+
* - 100 이상의 정수는 한자어 서수사 문자열로 변환합니다.
12+
*
13+
* @param num - 변환할 숫자
14+
* @return 변환된 서수사 문자열
15+
* @throws {Error} 지원하지 않는 숫자인 경우
16+
*
17+
* @example
18+
* seosusa(1); // '첫째'
19+
* seosusa(2); // '둘째'
20+
* seosusa(3); // '셋째'
21+
* seosusa(10); // '열째'
22+
* seosusa(11); // '열한째'
23+
* seosusa(12); // '열두째'
24+
* seosusa(13); // '열셋째'
25+
* seosusa(20); // '스무째'
26+
* seosusa(21); // '스물한째'
27+
* seosusa(30); // '서른째'
28+
* seosusa(40); // '마흔째'
29+
* seosusa(99); // '아흔아홉째'
30+
* seosusa(100); // '백째'
31+
*
32+
* @see https://es-hangul.slash.page/docs/api/seosusa
33+
*/
34+
export function seosusa(num: number): string {
35+
if (num === 0 || !Number.isInteger(num)) {
36+
throw new Error('유효하지 않은 입력입니다. 1이상의 정수만 지원합니다.');
37+
}
38+
39+
if (num >= 1 && num <= 99) {
40+
return `${getOrdinalWord(num)}째`;
41+
}
42+
43+
try {
44+
return `${numberToHangul(num)}째`;
45+
} catch (error) {
46+
throw new Error('유효하지 않은 입력입니다. 1이상의 정수만 지원합니다.');
47+
}
48+
}
49+
50+
function getOrdinalWord(num: number): string {
51+
if (hasProperty(SEOSUSA_SPECIAL_CASE_MAP, num)) {
52+
return SEOSUSA_SPECIAL_CASE_MAP[num];
53+
}
54+
55+
const tens = Math.floor(num / 10) * 10;
56+
const ones = num % 10;
57+
58+
const tensWord = hasProperty(SEOSUSA_MAP, tens) ? SEOSUSA_MAP[tens] : '';
59+
const onesWord = hasProperty(SEOSUSA_MAP, ones) ? SEOSUSA_MAP[ones] : '';
60+
61+
return `${tensWord}${onesWord}`;
62+
}

0 commit comments

Comments
 (0)