# Number letter counts

I'm trying to become more proficient in Python and have decided to run through the Project Euler problems.

In any case, the problem that I'm on (17) wants me to count all the letters in the English words for each natural number up to 1,000. In other words, one + two would have 6 letters, and so on continuing that for all numbers to 1,000.

I wrote this code, which produces the correct answer (21,124). However, I'm wondering if there's a more pythonic way to do this.

I have a function (num2eng) which translates the given integer into English words but does not include the word "and".

for i in range (1, COUNTTO + 1):
container = num2eng(i)
container = container.replace(' ', '')
print container
if i > 100:
if not i % 100 == 0:
length = length + container.__len__()  + 3 # account for 'and' in numbers over 100
else:
length = length + container.__len__()
else:
length = length + container.__len__()
print length

There is some repetition in my code and some nesting, both of which seem un-pythonic.

I'm specifically looking for ways to make the script shorter and with simpler loops; I think that's my biggest weakness in general.

• Your question isn't very specific; it's pretty open ended. We like specific programming questions here with a clear answer.
– Hiroto
Commented Feb 21, 2013 at 22:36
• Don’t call magic methods directly, just use len(container).
– poke
Commented Feb 21, 2013 at 22:39
• I know it doesn't relate to the programming question, but integers should never be written out or spoken with "and." "And" is used only for fractions. 1234 is "one thousand twenty-four" with no "and", but 23.5 is twenty-three and one half.
Commented Feb 21, 2013 at 23:07
• @askewchan: that's far from a universal standard, and I definitely don't think it rises to the level of "should never be". Even Americans, I believe, tend to include the "and" when writing on cheques. Unfortunately continued discussion would be off-topic, but for my part, let a thousand (and one) flowers bloom.
– DSM
Commented Feb 22, 2013 at 0:56
• @Hiroto Edited to make more specific, thanks! Commented Feb 22, 2013 at 5:23

Here are a few things:

• container.__len__() – You should never call magic methods directly. Just use len(container).
• As you don’t know to what amount you need to increment (i.e. your COUNTTO), it would make more sense to have a while loop that keeps iterating until you reach your final length result:

while length < 1000:
# do stuff with i
length += # update length
i += 1

• You could also make use of itertools.count:

length = 0
for i in itertools.count(1):
length += # update length
if length > 1000:
break

• not i % 100 == 0 – When you already have a operator (==) then don’t use not to invert the whole condition, but just use the inverted operator: i % 100 != 0
• Also having additional counting logic outside of your num2eng does not seem to be appropriate. What does that function do exactly? Shouldn’t it rather produce a real number using a complete logic? Ideally, it should be as simple as this:

length = 0
for i in itertools.count(1):
length += len(num2eng(i))
if length > 1000:
break

• I think the reason to have some logic outside of num2eng is to avoid counting the letters in 'hundred' and 'thousand' so many times.
Commented Feb 21, 2013 at 23:10
• @askewchan Huh? That doesn’t make much sense to me, as you still do that. Also I’d expect a function num2eng to return a complete written English representation of the number I pass in.
– poke
Commented Feb 21, 2013 at 23:13
• You're correct. The function num2eng returns the English representation of the numbers provided. For instance, 102 becomes one hundred two. It's doesn't include "and" though and the scope of the challenge required "and" to be included (e.g., one hundred and two), hence the addition. But it would makes sense to modify num2eng instead of making up for it here. Commented Feb 22, 2013 at 5:19
• Thank you so much for this feedback, this is exactly what I was looking for! Commented Feb 22, 2013 at 5:25

The more pythonic version:

for i in range(1, COUNTTO + 1):
container = num2eng(i).replace(' ', '')
length += len(container)
if i % 100:
length += 3   # account for 'and' in numbers over 100

print length

code golf:

print sum(len(num2eng(i).replace(' ', '')) + bool(i > 100 and i % 100) * 3
for i in range(1, COUNTTO + 1))

:)

Don't do that in real code, of course.

You can make it a bit shorter

length += len(container) + 3 if (i > 100 and i % 100) else 0
• i % 100 can't be 0 for 1 <= i <= 100. Commented Feb 21, 2013 at 22:46
• but for 1 <= i <= 100 there's no 'and' in the word
– Duc
Commented Feb 21, 2013 at 22:47
• Ah yes. Excuse me. Commented Feb 21, 2013 at 22:49