Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing letters from a list of both numbers and letters

Tags:

python

In a function I'm trying to write, the user enters a bunch of numbers e.g. "648392". I turn this string into a list like this: ['6', '4', '8', '3', '9', '2'].

I wanted to be able to do sums with these numbers so I was turning the numbers in the list into integers rather than strings. This all worked fine, however I also wanted the user to be able to enter letters, and then I would just remove them from the list - and this is where I'm stuck. For example a user entering "6483A2".

I can't check to see if an element is a digit with isDigit because the elements apparently have to be integers first, and I can't convert the elements in the list to integers because some of the elements are letters... I'm sure there's a simple solution but I am pretty terrible at python, so any help would be much appreciated!

like image 845
Tom F Avatar asked Dec 08 '22 13:12

Tom F


2 Answers

You can use str.translate to filter out letters:

>>> from string import letters
>>> strs = "6483A2"
>>> strs.translate(None, letters)
'64832'

There's no need to convert a string to a list, you can iterate over the string itself.

Using str.join, str.isdigit and list comprehension:

>>> ''.join([c for c in strs if c.isdigit()])
'64832'

or this as you want the sum of digits:

sum(int(c) for c in strs if c.isdigit())

Timing comparisons:

Tiny string:

>>> strs = "6483A2"
>>> %timeit sum(int(c) for c in strs.translate(None, letters))
100000 loops, best of 3: 9.19 us per loop
>>> %timeit sum(int(c) for c in strs if c.isdigit())
100000 loops, best of 3: 10.1 us per loop

Large string:

>>> strs = "6483A2"*1000
>>> %timeit sum(int(c) for c in strs.translate(None, letters))
100 loops, best of 3: 5.47 ms per loop
>>> %timeit sum(int(c) for c in strs if c.isdigit())
100 loops, best of 3: 8.54 ms per loop

Worst case, all letters:

>>> strs = "A"*100
>>> %timeit sum(int(c) for c in strs.translate(None, letters))
100000 loops, best of 3: 2.53 us per loop
>>> %timeit sum(int(c) for c in strs if c.isdigit())
10000 loops, best of 3: 24.8 us per loop
>>> strs = "A"*1000
>>> %timeit sum(int(c) for c in strs.translate(None, letters))
100000 loops, best of 3: 7.34 us per loop
>>> %timeit sum(int(c) for c in strs if c.isdigit())
1000 loops, best of 3: 210 us per loop
like image 119
Ashwini Chaudhary Avatar answered Jan 14 '23 22:01

Ashwini Chaudhary


You can filter things out of any iterable (including a string) with the filter function, or a comprehension. For example, either of these:

digits = filter(str.isdigit, input_string)
digits = (character for character in input_string if character.isdigit())

… will give you an iterable full of digits. If you want to convert each one to a number, either of these will do it:

numbers = map(int, filter(str.isdigit, input_string))
numbers = (int(character) for character in input_string if character.isdigit())

So, to get the sum of all the digits, skipping the letters, just pass either of those to the sum function:

total = sum(map(int, filter(str.isdigit, input_string)))
total = sum(int(character) for character in input_string if character.isdigit())

From your last paragraph:

I can't check to see if an element is a digit with isDigit because the elements apparently have to be integers first, and I can't convert the elements in the list to integers

First, it's isdigit, not isDigit. Second, isdigit is a method on strings, not integers, so you're wrong in thinking that you can't call it on the strings. In fact, you must call it on the strings before converting them to integers.

But this does bring up another option. In Python, it's often Easier to Ask Forgiveness than Permission. Instead of figuring out whether we can convert each letter to an int, and then doing it, we can just try to convert it to an int, and then deal with the possible failure. For example:

def get_numbers(input_string):
    for character in input_string:
        try:
            yield int(character)
        except TypeError:
            pass

Now, it's just:

total = sum(get_numbers(input_string))
like image 42
abarnert Avatar answered Jan 14 '23 20:01

abarnert