Last active
September 14, 2019 03:33
-
-
Save olragon/ffb05cf617f7d1d86d45261c3a71a175 to your computer and use it in GitHub Desktop.
Vietnamese number to text @ MIT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Convert number to text | |
* | |
* @param number | |
* @param prefix | |
*/ | |
function numberToText(number, prefix = '') { | |
// số, tên thường, tên hàng chục > 20, tên hàng chục > 10, =< 20 | |
let range1 = [ | |
[0, 'không', '', ''], | |
[1, 'một', 'mốt'], | |
[2, 'hai'], | |
[3, 'ba'], | |
[4, 'bốn'], | |
[5, 'năm', 'lăm', 'lăm'], | |
[6, 'sáu'], | |
[7, 'bảy'], | |
[8, 'tám'], | |
[9, 'chín'], | |
[10, 'mười'] | |
]; | |
let range2 = [ | |
[1000000000, 'tỷ'], | |
[1000000, 'triệu'], | |
[1000, 'ngàn'], | |
[100, 'trăm'], | |
]; | |
if (number < 0) { | |
number *= -1; | |
return numberToText(number, 'âm '); | |
} | |
let decPart = Number.isInteger(number) ? null : ('' + number).match(/\.([0-9]+)/)[1]; | |
let decTxt = !decPart ? '' : ' phẩy ' + decPart.split('').map(n => range1.find(r => r[0] == n)[1]).join(' '); | |
let intPart = Math.floor(number); | |
// không -> mười | |
if (intPart >= 0 && intPart <= 10) { | |
let r = range1.find(r => r[0] === intPart); | |
return prefix + r[1] + decTxt; | |
} | |
// mười -> mười chín | |
else if (intPart > 10 && intPart < 20) { | |
let r = range1.find(r => r[0] === intPart - 10); | |
let n = r[1]; | |
if (typeof r[3] === 'string') { | |
n = r[3]; | |
} | |
return prefix + 'mười ' + n + decTxt; | |
} | |
// hai mươi, ba mươi lăm, ... | |
else if (intPart >= 20 && intPart < 100) { | |
let result = prefix + ('' + intPart) | |
.split('') | |
.map(p => Number(p)) | |
.map((p, pIdx) => { | |
let r = range1.find(r => r[0] === p); | |
// ba mươi [mốt] <-- không phải một | |
if (pIdx > 0 && typeof r[2] === 'string') { | |
return r[2]; | |
} | |
return r[1] + (pIdx === 0 ? ' mươi' : ''); | |
}).join(' '); | |
return (result + decTxt).replace(/[ ]+/g, ' '); | |
} | |
// 100 -> ... | |
else { | |
let result = ''; | |
for (let i = 0; i < range2.length; i++) { | |
let r = range2[i]; | |
let roundedDiv = Math.floor(intPart / Number(r[0])); | |
if (roundedDiv >= 1) { | |
let remainder = intPart % Number(r[0]); | |
result += numberToText(roundedDiv); | |
result += ' ' + r[1]; | |
if (remainder) { | |
if (remainder < 10) { | |
result += ' lẻ'; | |
} | |
result += ' ' + numberToText(remainder); | |
} | |
break; | |
} | |
} | |
return prefix + result + decTxt; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const assert = require('assert'); | |
describe("numberToText", function () { | |
let testCases = [ | |
[0.000009, 'không phẩy không không không không không chín'], | |
[0, 'không'], | |
[10, 'mười'], | |
[11, 'mười một'], | |
[21, 'hai mươi mốt'], | |
[40.5, 'bốn mươi phẩy năm'], // not "bốn mươi không phẩy năm" | |
[10.0412, 'mười phẩy không bốn một hai'], | |
[99.9999, 'chín mươi chín phẩy chín chín chín chín'], | |
[103.05, 'một trăm lẻ ba phẩy không năm'], | |
[515, 'năm trăm mười lăm'], | |
[567241, 'năm trăm sáu mươi bảy ngàn hai trăm bốn mươi mốt'], | |
[1567241, 'một triệu năm trăm sáu mươi bảy ngàn hai trăm bốn mươi mốt'], | |
[21396541323.005, 'hai mươi mốt tỷ ba trăm chín mươi sáu triệu năm trăm bốn mươi mốt ngàn ba trăm hai mươi ba phẩy không không năm'], | |
[1000000000000, 'một ngàn tỷ'] | |
]; | |
testCases.forEach((testCase) => { | |
it(`can read ${testCase[0]} as ${testCase[1]}`, function () { | |
assert.strictEqual(numberToText(testCase[0] as number), testCase[1]); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment