Introduction
I was going through the articles and ran into an article that
showed the conversion of number to words, such as 12 to Twelve. I though let me try same thing but in a
different way. I am not a programmer but like to monkey around with codes.
I have/will be adding other languages that I may NOT know. For example, Indonesian which I recently added can’t even pronounce the converted words; yet made the converter. Point is, please verify and let me know if any improvements are needed. Thanks.
Anyways, the Converter
class allows us to convert the numbers into words. Let the picture talk!
Background
Before digging into the code let’s see how the numbers are
read in word
In English Style (US):
123,123,000 = One hundred twenty three
million one hundred twenty three thousand
123,000,000 = One
hundred twenty three million
123,000
= One hundred twenty three thousand
As we can see the three set of numbers are
always spelled same way and then the modifier, like million is appended. In
English style numbers are aggregated in a group of three. In Asian style it’s
little bit different. The last (towards right) three are grouped together and
then rests of the numbers are aggregated with two words.
In South Asian Style:
12,12,112 = Twelve lakh twelve thousand one hundred twelve
12,00,000 = Twelve lakh
12,000 = twelve thousand
112 =
one hundred twelve
In Indonesian Style:
123,123,100 = Seratus Dua Puluh Tiga Juta Seratus Dua Puluh Tiga Ribu
123,000,000 = Seratus Dua Puluh Tiga Juta
123,000 = Seratus Dua Puluh Tiga Ribu
Code Flow
The
absolute common thing between these two systems is the digit. 1 is one, 9 is
nine and 20 is twenty in both. And the difference
is how the higher numbers are grouped and read. To facilitate these I created
arrays. These arrays are the heart of the conversion. The tricky part is to find
the index so that we can use these arrays.
Private SingleDigitStringArray() As String = _
{"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"}
Private DoubleDigitsStringArray() As String = _
{"", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}
Private TenthDigitStringArray() As String = _
{"Ten", "Eleven", "Tweleve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", _
"Eighteen", "Nineteen"}
Private HigherDigitEnglishStringArray() As String = _
{"", "", "Hundred", "Thousand", "Million", "Billion", "Trillion", "Quadrillion", "Quintillion"}
Private HigherDigitAsianStringArray() As String = _
{"", "", "Hundred", "Thousand", "Lakh", "Karod", "Arab", "Kharab"}
Private EnglishCodeArray() As String = {"1", "22", "3"}
Private AsianCodeArray() As String = {"1", "22", "3", "4", "42", "5", "52", "6", "62", "7", "72", "8", "82"}
Private IndonesianCodeArray() As String = {"1", "22", "3", "4", "42", "420"}
Private SingleDigitIndonesianArray() As String = {"", "Satu", "Dua", "Tiga", "Empat", "Lima", "Enam", _
"Tujuh", "Delapan", "Sembilan"}
Private DoubleDigitIndonesianArray() As String = {"Puluh", "Belas"}
Private HigherDigitIndonesianStringArray() As String = {"", "", "Ratus", "Ribu", "Juta", "Miliar", "Triliun"}
The entire process is basically array manipulation. The primary
goal is to find the correct index corresponding to the number and its position and
then pulling the corresponding word out of the array shown above.
Let’s get the ball rolling. In English
conversion method, we work 3 digits at a time from right side and then affix
the thousand/million as necessary and loop until we are done, as shown below:
Do
concatHigherDigitString = False
If i > 3 Then concatHigherDigitString = True
If newAmount.Length >= 4 Then newAmount = amountString.Substring(amountString.Length - i, 3)
End If
If concatHigherDigitString AndAlso CInt(newAmount) <> 0 Then
result = ThreeDigitsConverter(CInt(newAmount)) & " " & _
HigherDigitEnglishStringArray(i / 3 + 1) & " " & result
Else
result = ThreeDigitsConverter(CInt(newAmount))
End If
workedStringLength += newAmount.Length
newAmount = amountString.Substring(0, amountString.Length - workedStringLength)
i += 3
Loop Until amountString.Length <= workedStringLength
In
both of the method, we first convert the given number to string and then to an
array, for instance, 1234 to “1234” and then to {1, 2, 3, 4}, using following
simple code. I believe there must be a better way of doing this…
Dim amountArray(amountString.Length - 1) As Integer
For i As Integer = amountArray.Length To 1 Step -1
amountArray(i - 1) = amountString.Substring(i - 1, 1)
Next
Now we work on digit by digit but backwards as the digit in
unit place remains in unit place all the time.
Things get little complicated when working with numbers like 12 because
it’s not read like onetwo however as Twelve. Similarly, 22 is not twotwo but
Twenty two. Likewise, 10001 is not Ten thousand zero hundred one but it’s Ten thousand one.
Keeping all these in minds, following loop will find the right index and then
from the words library (fields array that we talked about earlier) we convert
the digits to word.
Primary method for number to English word conversion
Converting to English method is kind of straight forward compared to other methods. There aren't that many unique situation so it's not that difficult to find the correct index of required array.
For i As Integer = amountArray.Length To 1 Step -1
j = amountArray.Length - i
digit = amountArray(j)
codeIndex = EnglishCodeArray(i - 1)
higherDigitEnglishString = HigherDigitEnglishStringArray(CInt(codeIndex.Substring(0, 1)) - 1)
If codeIndex = "1" Then result = result & SingleDigitStringArray(digit)
ElseIf codeIndex.Length = 2 And digit <> 0 Then
If digit = 1 Then Dim suffixDigit As Integer = amountArray(j + 1)
result = result & separator & TenthDigitStringArray(suffixDigit) & " " & higherDigitEnglishString
i -= 1
Else
If Me.Style = ConvertStyle.EnglishUK Then
result = result & separator & DoubleDigitsStringArray(digit) & "-" & higherDigitEnglishString
ElseIf Me.Style = ConvertStyle.EnglishUS Then
result = result & separator & DoubleDigitsStringArray(digit) & " " & higherDigitEnglishString
End If
End If
ElseIf digit <> 0 Then
If Me.Style = ConvertStyle.EnglishUK Then andSeparator = " and"
result = result & separator & SingleDigitStringArray(digit) & " " & higherDigitEnglishString _
& andSeparator
End If
separator = " "
Next
Primary method for number to Indonesian word conversion
Indonesian style is somewhat similar to English style but there are several unique cases which makes it difficult. The challenging part for was a number when starts with "1". When a numbers starts with 1, the two words merge to one but it's not just the simple concatenation. The funny part is I can't even pronounce the converted word correctly!
For i As Integer = amountArray.Length To 1 Step -1
j = amountArray.Length - i
digit = amountArray(j)
codeIndex = IndonesianCodeArray(i - 1)
higherDigitIndonesianString = HigherDigitIndonesianStringArray(CInt(codeIndex.Substring(0, 1)) - 1)
If codeIndex = "1" Then result = result & separator & SingleDigitIndonesianArray(digit)
ElseIf codeIndex.Length = 2 And digit <> 0 Then Dim suffixDigit As Integer = amountArray(j + 1)
If digit = 1 And suffixDigit < 2 Then result = result & separator & "Se" & DoubleDigitIndonesianArray(suffixDigit).ToLower & " " & _
higherDigitIndonesianString
i -= 1
ElseIf digit = 1 And suffixDigit >= 2 Then result = result & separator & SingleDigitIndonesianArray(suffixDigit) & " " & _
DoubleDigitIndonesianArray(1) & " " & higherDigitIndonesianString
i -= 1
ElseIf suffixDigit <> 0 Then result = result & separator & SingleDigitIndonesianArray(digit) & " " & _
DoubleDigitIndonesianArray(0)
Else result = result & separator & SingleDigitIndonesianArray(digit) & " " & _
DoubleDigitIndonesianArray(0) & " " & higherDigitIndonesianString
End If
ElseIf digit <> 0 Then If digit = 1 Then result = result & separator & "Se" & higherDigitIndonesianString.ToLower
Else
result = result & separator & SingleDigitIndonesianArray(digit) & " " & higherDigitIndonesianString
End If
End If
separator = " "
Next
During the process a space or two get attached in between
the words so for the cleanup I use the RegEx
as:
Private Function RemoveSpaces(ByVal word As String) As String
Dim regEx As New System.Text.RegularExpressions.Regex(" ")
Return regEx.Replace(word, " ").Trim
End Function
There we go, this will convert the numbers to digits!
Number formatting (or grouping):
There
is another public function FormatNumber
which basically calls a private FormatNumberPerLanguage
in the Converter
class. This FormatNumberPerLanguage
will format
group based on the provided regional name. A simple use of CultureInfo
class.
Private Function FormatNumberPerLanguage(ByVal culterInfoName As String)
Dim ci As New System.Globalization.CultureInfo(culterInfoName)
ci.NumberFormat.NumberDecimalDigits = 0
Return Me.Amount.ToString("N", ci)
End Function
Using the code
Simply
create a new instance of the converter and pass the number you want to convert
and the conversion style and call the Convert method.
Blocks of code should be set as style "Formatted"
like this:
Dim converter As New Converter(number, converter.ConvertStyle.EnglishUS)
Me.RichTextBox1.Text = converter.Convert
converter = New Converter(number, NumberToWords.Converter.ConvertStyle.Asian)
Me.RichTextBox2.Text = converter.Convert
Points of Interest
These
two arrays are worth mentioning
Private EnglishCodeArray() As String = {"1", "22", "3"}
Private AsianCodeArray() As String = {"1", "22", "3", "4", "42", "5", "52", "6", "62", "7", "72", "8", "82"}
Private IndonesianCodeArray() As String = {"1", "22", "3", "4", "42", "420"}
…these two arrays help to determine whether a digit is in unit
place or, tenth place and so on… In the above array, double digit like “22” will
always represent the 10th place. “4” will represent thousand and “42” will
represent ten thousand.
Somewhere I have a feeling I have not done enough testing so comments
are actually what I am expecting. Thanks!