I have developed the following short JavaScript function to convert numbers to the Indian Numbering System formats; both the Official Numbering System and the Common-Use System (i.e. using the Lakh-Crore System) using the method of converting the number into strings of Siptlets and Duplets.
Based on this wiki article that explains the Indian Numbering System I have created the following summary table that translates 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 code 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:
Numbers to be converted can be passed as Numbers or Strings.
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).
It also serves as an example of how to call the function for the whole part of numbers and fractional parts of the number.
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.
Some internals workings of the functions are:
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.map((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.map((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;}}