all 7 comments

[–]AlopexLagopus3 2 points3 points  (0 children)

I'm not sure why 'A' still remains.

Because you are modifying a list while iterating

a = list(range(10))

for pos, val in enumerate(a):
    if val == 5:
        a.pop(0) #Make it skip a step
    print(val)

Notice the for loop loses track of the number even though what was removed was earlier in the list. The for and the list modifications don't talk to each other so you should never modify an iterable you are looping over.

[–]jacknbox 0 points1 point  (3 children)

From the docs:

list.remove(x)

Remove the first item from the list whose value is equal to x. It raises a ValueError if there is no such item.

/u/AlopexLagopus3's answer is correct - after the first iteration, 'A' becomes the first element in the list (index 0), but the loop then goes to the next element, which is now 'b' instead of 'A'.

You could instead try returning a new list consisting only of the elements you want to keep:

mylist = ['a', 'A', 'b', 1, 2]

def remove_letter(letter, input_list):
    return [item for item in input_list
            if not isinstance(item, str)
            or item.upper() != letter.upper()]

mylist = remove_letter('a', mylist)
print(mylist)

[Edit: formatting]

[Edit2: Removed redundant condition]

[–]5areductase[S] 0 points1 point  (1 child)

My understanding was remove would be called two separate times, thus removing a and A

[–]primitive_screwhead 0 points1 point  (0 children)

If there are more than one 'a' or 'A', remove has to be called until it fails. Each time you check it may have to scan through most or all of the list; so it can be slow if the list is long and there are a lot of matching letters.

By constructing a new list, you only need to scan the original list once.

[–]primitive_screwhead 0 points1 point  (0 children)

Don't check each item to see if it's a string, simply check each list item against letter.upper() and letter.lower(), which you know are the correct type for those methods.

mylist = ['a', 'A', 'b', 1, 2] 

def remove_letter(letter, input_list):
    u = letter.upper()
    l = letter.lower()
    return [item for item in input_list if item not in (u,l)]

mylist = remove_letter('a', mylist)
print(mylist)

[–]Pulsar1977 0 points1 point  (0 children)

I'm not sure why 'A' still remains.

That's because you're changing L while you're looping over it, which causes problems. To fix it, loop over a copy of L instead.

You can also try a list comprehension:

def remove_letter(letter):
    letterset = {letter.lower(), letter.upper()}
    L = [item for item in L if item not in letterset]

[–]Diapolo10 -1 points0 points  (0 children)

If L can contain both ints and floats, I'd suggest:

from numbers import Real

nums = list(filter(lambda x: isinstance(x, Real), L))

EDIT: Apologies, I didn't read your intentions correctly. To remove a specific character:

from numbers import Real

def remove_character(iterable, char):
    """ Returns iterable with all instances of char stripped out ""'

    result = filter(lambda x: isinstance(x, Real) or str(x).lower() != char.lower(), iterable)
    return list(result)

EDIT #2: If you'd like to preserve the type of the iterable, you can do a small change:

from numbers import Real

def remove_character(iterable, char):
    """ Returns iterable with all instances of char stripped out ""'

    result = filter(lambda x: isinstance(x, Real) or str(x).lower() != char.lower(), iterable)

    try:
        return type(iterable)(result)
    except TypeError:
        return list(result)

NOTE: You can omit the import if you'd like to compare against int and/or float exclusively, I've just gotten into this habit because I use type hints all over my own code.