I am required to produce an accurate and reliable method for converting numbers into words for use in official document in the Indian Numbering System (as used in the countries of the Indian subcontinent).
The result is supposed to be used for any subject that needs counting (not only currencies).
The task requires that, in addition to the Official Numbering System, an option be given to produce results using a Common-Use System (i.e. using the Lakh-Crore System).
I have been searching and googling this for the past week including stackoverflow.
The following StackOverflow (javascript-tagged) carry questions and posts in their title and/or content have reference to Indian numbers or currencies but are different and are very old (most over 7 to 9 years old) and do not answer my question or help me (and are mostly conflicting in results which add to the confusion):
- How to Convert numbers to word in Indian Currency Format All together different matter.
- Javascript function to convert Indian currency numbers to words with paise support Different topic.
- number to words (Indian numbering system) using javascript
The problem I have is that I am not used to such a numbering system and cannot confirm that any coding I do will produce the correct result as I am not from that region.
Based on this wiki article that explains the Indian Numbering System I have created the following summary table that (hopefully correctly) translates (based on my understanding) the same into a steps/flow logic that can then be translated into appropriate code.
So, based on the above (and my understanding of the subject), I have worked out the javascript function below to handle:
- The Official Numbering System, and
- The Common-Use Numbering System, and
- If needed Indian Currency.
I have tried to (as far as possible) use ES6 keywords and functions.
When generating the Common-Use text, a "comma" is inserted between each Siptlet text as the output is long and difficult to follow. However, this comma can be deleted on the 8th line of the coded if not desired.
I have also included 2 test case codes to test both Numbering Systems.
The function can be called for the Common-Use as follows:
integerToWordsInd("2222300000")); // Two Hundred Twenty-Two Crore, Twenty-Three Lakh
and for the Official System:
integerToWordsInd ("2222300000",true); // Two Arab Twenty-Two Crore Twenty-Three Lakh
An additional function numberCurrencyInd() is also added to convert the number into Indian Currency “Rupees and Paisa”, and may use the Official or Common-Use System as an option using the calling parameters (similar to the number conversion function).
numberCurrencyIn(3002900000.50); //Three Hundred Crore, Twenty-Nine Lakh Rupees and Fifty Paisa
// or
numberCurrencyIn(3002900000.50,true); // Three Arab Twenty-Nine Lakh Rupees and Fifty Paisa
Two (2) further rest cases are added to test currency generation under both Systems.
I, however, need help and assistance in the following, which I could not find answers to in other places:
- Is the Numbering System correct by using the Lakh-Crore System?
- Is the output result what is being used in the Indian subcontinent and correct.
- When to use the Official System and the Common-Use System (lakh-crore).
- What is the scale name after "shankh" as I ran out of scales.
- Do we write plural scale names with an additional “s” or not? i.e. Do we say “thirty crores, twenty lakhs” or “thirty crore, twenty lakh” like in the English system? The same goes to Rupees and Pisas.
- Is it correct or appropriate to have the Common-Use System as the default for the function call?
- Is the sequence of the Common-Use System by repeating the word “crore” for increasing numbers correct; i.e. “crore crore” then “crore crore crore”, etc. ?
Thanks in advance for any help offered.
Some Internals Workings of the Functions are:
For those interested:
The following line converts the number into array elements of Siptlets (7s) (fixed size 7 digits) for use in the Common-Use Numbering System:
Num = ("0".repeat(6*(Num+="").length % 7) +Num).match(/.{7}/g);
The following line converts the number into array elements of 1 Triplet followed by Duplets for use in both the Official and Common-Use Numbering System:
Num = Num> 999 ? [...(Num.slice(0,-3).match(/.{2}/g).map(e => "0"+e)),(Num.slice(-3))] :
[("00"+Num).substr(-3)];
If the Common-Use System is requested, then the number is first converted into Siptlets (7's) then the inner function is called for each Siptlet to generate the words using the Triplet/Duplets.
However, if instead, the Official System is requested then the inner function is called immediately to generate the words using the Triplet/Duplets.
/*********************************************************************
* @function : integerToWordsInd()
* @purpose : Converts Unsigned Integers to Indian Numeral Words
* With options for either the Official or the
* Crore-Lakh Counting Systems
* @version : 1.00
* @author : Mohsen Alyafei
* @date : 07 July 2020
* @param : {number} [integer numeric or string]
* @param : Optional {boolean} [Official]
* 0 = Use Crore-Lakh Counting System (default)
* Non 0 = Use the Official System
* @returns : {string} The wordified number string
**********************************************************************/
var Table_0_19 = ["","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen","Sixteen","Seventeen","Eighteen","Nineteen"],
Table_20_90= ["","","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"],
Table_Scale= ["","Thousand","Lakh","Crore","Arab","Kharab","Neel","Padma","Shankh","Samudra","Antya","Madhyam","Paraardh","***","***"];
//===================================================================
function integerToWordsInd(Num=0 , Official=0) {
if (Num===0) return "Zero";
if (Official) return Siptlets(Num); // Return Official Numbering System text
let NumWords="";
Num = ("0".repeat(6*(Num+="").length % 7) +Num).match(/.{7}/g); // Create Siptlets Array
return Num.forEach((Siptlet, ScalePos) => { // Return Commmon-Use Numbering System text
let [Scale,SWords] = [(Table_Scale[3]+" ").repeat(Num.length-ScalePos-1).trimRight(), Siptlets(Siptlet)];
NumWords +=(NumWords && SWords ? ", " : "") +SWords +(Scale ? " " : "") +Scale;
}), NumWords;
//===================================================================
function Siptlets(Num, NumWords="") { // Core function (Called for both Systems)
(Num+="").length-3 & 1 && (Num="0"+Num);
Num = Num> 999 ? [...Num.slice(0,-3).match(/.{2}/g).map(e => "0"+e),(Num.slice(-3))]:[("00"+Num).substr(-3)];
return Num.forEach((Duplet,ScalePos) => {if (+Duplet) {
let [Hyphen,Hundreds,Tens,Scale] = [+Duplet[2] ? "-" : "",+Duplet[0],+Duplet.substr(1),Table_Scale[Num.length-ScalePos-1]];
NumWords += (NumWords ? " " : "") + (Hundreds ? Table_0_19[Hundreds] + " Hundred" :"") +
(Hundreds && Tens ? " " : "") + (Tens< 20 ? Table_0_19[Tens] :
Table_20_90[+(Duplet[1])] + Hyphen + Table_0_19[+Duplet[2]]);
NumWords += (NumWords && Scale ? " " : "") + Scale;
}}), NumWords;}
}
//===================================================================
//===================================================================
// Extra Function if needed for Indian Currency
// Uses same input parameters as the above main function
//===================================================================
function numberCurrencyIn(Num=0 , Official=0) {
let n= (Num+"").split(0.1.toLocaleString().substr(1,1)); // Number and Fraction parts
n.length!==2 && (n[1]= ""); // No fraction
Num= n[0];
let Nw="", Fw="", Frc = (n[1]+"00").substring(0,2); // Limit to 2 Decimal Places
Num && (Nw= integerToWordsInd(Num,Official)); // Convert the Whole Number
Frc && (Fw= integerToWordsInd(Frc,Official)); // Convert the Fractional Part
return (Nw ? Nw:"") + (Nw ? " Rupees":"") + (Nw && Fw ? " and ":"") + (Fw ? Fw+" Paisa":""); // Join together
}
//===================================================================
//===================================================================
// Test Cases
//===================================================================
// 1. Test Numbers under Common-Use Numbering System
//===================================================================
var r=0; // test tracker
r |= testN(50,"Fifty");
r |= testN(12000,"Twelve Thousand");
r |= testN(777000,"Seven Lakh Seventy-Seven Thousand");
r |= testN(550001,"Five Lakh Fifty Thousand One");
r |= testN(12345678,"One Crore, Twenty-Three Lakh Forty-Five Thousand Six Hundred Seventy-Eight");
r |= testN(123456789,"Twelve Crore, Thirty-Four Lakh Fifty-Six Thousand Seven Hundred Eighty-Nine");
r |= testN(1234567890,"One Hundred Twenty-Three Crore, Forty-Five Lakh Sixty-Seven Thousand Eight Hundred Ninety");
r |= testN(12345678900,"One Thousand Two Hundred Thirty-Four Crore, Fifty-Six Lakh Seventy-Eight Thousand Nine Hundred");
if (r==0) console.log("Test Case 1 Numbers (Common-Use Numbering System) Passed.");
//===================================================================
// 2. Test Numbers under Official Numbering System
//===================================================================
var r=0; // test tracker
r |= testN(50,"Fifty");
r |= testN(12000,"Twelve Thousand",true);
r |= testN(777000,"Seven Lakh Seventy-Seven Thousand",true);
r |= testN(550001,"Five Lakh Fifty Thousand One",true);
r |= testN(12345678,"One Crore Twenty-Three Lakh Forty-Five Thousand Six Hundred Seventy-Eight",true);
r |= testN(123456789,"Twelve Crore Thirty-Four Lakh Fifty-Six Thousand Seven Hundred Eighty-Nine",true);
r |= testN(1234567890,"One Arab Twenty-Three Crore Forty-Five Lakh Sixty-Seven Thousand Eight Hundred Ninety",true);
r |= testN(12345678900,"Twelve Arab Thirty-Four Crore Fifty-Six Lakh Seventy-Eight Thousand Nine Hundred",true);
if (r==0) console.log("Test Case 2 Numbers (Official Numbering System) Passed.");
//===================================================================
// 3. Test Currency under Common-Use Numbering System
//===================================================================
var r=0; // test tracker
r |= testC(1,"One Rupees");
r |= testC(2.0,"Two Rupees");
r |= testC(2.01,"Two Rupees and One Paisa");
r |= testC(0.3,"Thirty Paisa");
r |= testC(.3,"Thirty Paisa");
r |= testC(3002900000.50,"Three Hundred Crore, Twenty-Nine Lakh Rupees and Fifty Paisa");
r |= testC(220000,"Two Lakh Twenty Thousand Rupees");
if (r==0) console.log("Test Case 3 Currency (Common-Use Numbering System) Passed.");
//===================================================================
// 4. Test Currency under Official Numbering System
//===================================================================
var r=0; // test tracker
r |= testC(3002900000.50,"Three Arab Twenty-Nine Lakh Rupees and Fifty Paisa",true);
r |= testC(55000000000,"Fifty-Five Arab Rupees",true);
if (r==0) console.log("Test Case 4 Currency (Official Numbering System) Passed.");
//===================================================================
function testN(n,tobe,f) {let r = integerToWordsInd(n,f);
if (r !== tobe) {console.log(`${n} Output : ${r}\n${n} Should be: ${tobe}`);return 1;}}
function testC(n,tobe,f) {let r = numberCurrencyIn(n,f);
if (r !== tobe) {console.log(`${n} Output : ${r}\n${n} Should be: ${tobe}`);return 1;}}