I've created a class that translates an integer into words in an English (not British) format. My first human language is not English, and I don't have a good idea of a truly right way to read numbers, so I followed the exact meaning of the numbers (e.g. 1024 = one thousand twenty-four, not ten twenty-four as read by some humans).
public class NumsToWords {
private static String[] ones = {"","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"};
private static String[] tens = {"","","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"};
private static String hundred = " hundred ";
private static String[] all = {"","thousand","million","billion","trillion","quadrillion"};
public static String translate(String number) {
StringBuilder res = new StringBuilder();
StringBuilder mutableNumber = new StringBuilder(number);
int numberLength = number.length();
// totalTypes is my way of separating the digits in to groups that has three digits each, a ones, tens and hundreds. For example 123456 has two. A thousands part (123) and normal digits (456)
int totalTypes = numberLength/3;
// leadingDigits is my way of finding out of not all parts of a number is complete. That is. in 12345, 12 is the thousand part, not 123.
int leadingDigits = numberLength%3;
if (leadingDigits > 0) {
insertWords(res, mutableNumber, leadingDigits, totalTypes);
}
totalTypes--;
for (int i = leadingDigits; i < numberLength; i+= 3) {
insertWords(res, mutableNumber, 3, totalTypes--);
}
return res.toString().trim();
}
// translate numbers that are present in all types (ones, tens and hundreds)
private static StringBuilder translateHundreds(String number) {
StringBuilder res = new StringBuilder();
if (number.equals("0")) {
res.append("zero");
} else {
int intNumber = Integer.parseInt(number);
if (intNumber >= ones.length) {
StringBuilder digits = new StringBuilder(number);
if (digits.length() == 2) {
res.append(tens[next(digits)]);
String one = ones[next(digits)];
if (!one.isEmpty()) {
res.append("-"+one);
}
}
if (digits.length() == 3) {
int hundredVal = next(digits);
res.append(hundredVal == 0 ? "" : ones[hundredVal]+hundred).append(translateHundreds(number.substring(1)));
}
} else {
res.append(ones[intNumber]);
}
}
return res;
}
private static void insertWords(StringBuilder res, StringBuilder number, int end, int index) {
StringBuilder trans = translateHundreds(number.substring(0, end));
if (trans.length() > 0) {
res.append(trans).append(" "+all[index]+" ");
}
number.delete(0, end);
}
// read values from part of a number not exceeding three digits
private static int next(StringBuilder str) {
int res = Integer.parseInt(str.substring(0,1));
str.delete(0, 1);
return res;
}
}
- I am manipulating characters all over the code, so I used
StringBuilder
extensively to avoid wasting objects. Unfortunately, I find some parts of my code is nearly very hard to read and it seems I'm not really saving memory because many of thoseStringBuilder
objects will eventually be converted to String most of the time. What should I do? Is the mutability advantage ofStringBuilder
over a normalString
worth it? Am I usingStringBuilder
correctly - Many of my variable names are meaningless. I really don't know what name I should give them usually when converting a string containing numbers into a real integer. Is the naming of my variables in this code acceptable?
- I think there are too much casting between types all over the code. How can I prevent this?
- Overall, I think my code is extremely inefficient in terms of performance and memory. It looks absolutely ugly too. How can I improve this code?