My code works, but the aesthetic of the code seems to need some real work. The logic is simple enough:
- Create three lists of words that will be used throughout the conversion
- Create three functions:
- One for sub-1000 conversion
- Another for 1000-and-up conversion
- Another function for splitting and storing into a list of numbers above 1000.
First the code:
# Create the lists of word-equivalents from 1-19, then one for the tens group.
# Finally, a list of the (for lack of a better word) "zero-groups".
ByOne = [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen"
]
ByTen = [
"zero",
"ten",
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
]
zGroup = [
"",
"thousand",
"million",
"billion",
"trillion",
"quadrillion",
"quintillion",
"sextillion",
"septillion",
"octillion",
"nonillion",
"decillion",
"undecillion",
"duodecillion",
"tredecillion",
"quattuordecillion",
"sexdecillion",
"septendecillion",
"octodecillion",
"novemdecillion",
"vigintillion"
]
strNum = raw_input("Please enter an integer:\n>> ")
# A recursive function to get the word equivalent for numbers under 1000.
def subThousand(inputNum):
num = int(inputNum)
if 0 <= num <= 19:
return ByOne[num]
elif 20 <= num <= 99:
if inputNum[-1] == "0":
return ByTen[int(inputNum[0])]
else:
return ByTen[int(inputNum[0])] + "-" + ByOne[int(inputNum[1])]
elif 100 <= num <= 999:
rem = num % 100
dig = num / 100
if rem == 0:
return ByOne[dig] + " hundred"
else:
return ByOne[dig] + " hundred and " + subThousand(str(rem))
# A looping function to get the word equivalent for numbers above 1000
# by splitting a number by the thousands, storing them in a list, and
# calling subThousand on each of them, while appending the correct
# "zero-group".
def thousandUp(inputNum):
num = int(inputNum)
arrZero = splitByThousands(num)
lenArr = len(arrZero) - 1
resArr = []
for z in arrZero[::-1]:
wrd = subThousand(str(z)) + " "
zap = zGroup[lenArr] + ", "
if wrd == " ":
break
elif wrd == "zero ":
wrd, zap = "", ""
resArr.append(wrd + zap)
lenArr -= 1
res = "".join(resArr).strip()
if res[-1] == ",": res = res[:-1]
return res
# Function to return a list created from splitting a number above 1000.
def splitByThousands(inputNum):
num = int(inputNum)
arrThousands = []
while num != 0:
arrThousands.append(num % 1000)
num /= 1000
return arrThousands
### Last part is pretty much just the output.
intNum = int(strNum)
if intNum < 0:
print "Minus",
intNum *= -1
strNum = strNum[1:]
if intNum < 1000:
print subThousand(strNum)
else:
print thousandUp(strNum)
Sample run:
>>>
Please enter an integer:
>> 95505896639631893
ninety-five quadrillion, five hundred and five trillion, eight hundred and ninety-six billion, six hundred and thirty-nine million, six hundred and thirty-one thousand, eight hundred and ninety-three
>>>
Issues:
Basically, the peeves I'm having are as follows:
- My first two functions seems to be taking up too many lines. The first one,
subThousand
, seems to be this way because of the check made against the last digit of a number from 20 to 99. The logic I applied was, if it ends in0
, use theByTen
list. If it doesn't combinedByTen
andByOne
. While effective, I feel like it could use some real work since I think it qualifies as a DRY-violation. Even the final part checking for numbers from 100 to 999 seems to follow the same pattern. Alas, I've tried paring it down but I've honestly hit a roadblock on this one insofar as trying to come up with a creative and clean solution. My second function,
thousandUp
is quite the disaster. I tried coming up with a looping function that creates a list of one to three-digit numbers, so that I can callsubThousand
on each of them from the front to the back (hence thearrZero[::-1]
. At the same time, after converting each element in the list to a word, I concatenate it with the appropriate equivalent in thezGroup
list, or the list of the "zero-groups". However, I personally can't find a safer and more precise way of landing on the "correct" spot in thezGroup
list to start concatenating.To get around this, I took the length of the
splitByThousands
array, adjusted for0
-index, and used it to get the appropriate "zero-append" (zap
). Before the loop ends, I subtract one from its current value so that it's adjusted accordingly.In addition, as I'm attempting to make the output as clean as possible, I add a
" "
to thewrd
variable so it doesn't concatenate poorly with the zero-append, as well as add a", "
to the zero-append to separate it from the next zero-group. However, there will be instances that there are no values for some zero-groups. To avoid showing stuff likezero million
, I added a check inside. This is the part that makes me die a little inside:for z in arrZero[::-1]: wrd = subThousand(str(z)) + " " zap = zGroup[lenArr] + ", " if wrd == " ": break elif wrd == "zero ": wrd, zap = "", "" resArr.append(wrd + zap) lenArr -= 1
It works, but it just doesn't look good. Is it possible to do this in list-comprehension form or a better for-loop without turning it into more confusing mush?
I must admit as well that the last part of the code sucks a little. I've done a lot of str-->int
conversions inside the functions, then I did one more outside of them. On top of that, my approach to negative numbers is hackish at best (print "Minus"
).