Filtering out lists and only keeping integers in python

Viewed 283

I am trying to take a list with a mix of integers and strings, filter out the strings in the list, and only keep the integers. An example of a list I might have to filter:

filter_list([1,2,'a','b'])

Here is my code:

new_list = list()
def filter_list(l):
    for step in l:
        if type(l[step]) == int:
            new_list.append(int(l[step]))
        else:
            pass
    return new_list

However, I am getting the error:

Traceback (most recent call last):
  File "tests.py", line 3, in <module>
    test.assert_equals(filter_list([1,2,'a','b']),[1,2])
  File "/workspace/default/solution.py", line 4, in filter_list
    if type(l[step]) is int:
TypeError: list indices must be integers or slices, not str

What am I doing wrong? The traceback is coming from running the test file which tests to see if my code works, but the actual error is from my code.

3 Answers

Other people already provided solutions, but they didn't explain, why your didn't work.

Your list is [1,2,'a','b']. Python throws exception at element 'a'. If you run this code:

list = [1,2,'a','b']
for step in list:
   print(step)

Your output would be:

1
2
a
b

As you can see 3rd element of your list is string - that's why you get

TypeError: list indices must be integers or slices, not str

To get index of element use enumerate:

list = [1,2,'a','b']
for index, value in enumarate(list):
   print(f"{index}: {value}")

Output:

0: 1
1: 2
2: a
3: b

Your code with enumarate:

new_list = list()
def filter_list(l):
    for step, value in enumarate(l):
        if type(l[step]) == int:
            new_list.append(int(l[step]))
        else:
            pass
    return new_list

A for loop in python iterates over the elements, not the indices, of an iterable object. That means that step takes on the values 1, 2, 'a', 'b', not 0, 1, 2, 3. In other languages, this sort of iteration is sometimes called foreach.

While the first two list elements are valid indices, the last two are not. Also, l[2] is 'a', which is not an integer. The most basic fix is to fix your indexing. The example below also initializes the new list inside the function, since you probably don't want to keep appending to the same global list (run the function twice to see what I mean).

def filter_list(l):
    new_list = []
    for item in l:
        if type(item) == int:
            new_list.append(item)
    return new_list

You can also make your check of integerness more idiomatic. If you only want to keep objects that are already integers, use isinstance(item, int) rather than type(item) == int. If you want to allow things like the string '123' to pass through, use int(item) in a try-except instead:

def filter_list(l):
    new_list = []
    for item in l:
        try:
            new_list.append(int(item))
        except TypeError, ValueError:
            pass
    return new_list

The most idiomatic way to express the version with a conditional is to use a list comprehension:

def filter_list(l):
    return [item for item in l if isinstance(item, int)]

Try this instead:

list(filter(lambda x: isinstance(x, int), [1,2,'a','b']))

Python's built in filter function has two arguments, first one is a function that you want to apply the filter with and the second one is the object being filtered.

output:

[1, 2]
Related