0

I am trying to remove multiple occurrences of a value from a list.The output does not remove any of the desired items.

def rem(a,li):
try:
    while a in li == True:
        li.remove(a)
    print('Updated list: ',li)
except ValueError:
    print(a,' is not located in the list ',li)

Example to trying out function:

L = [1,2,3,45,3,2,3,3,4,5]

rem(2,L)

Output: Updated list: [1, 2, 3, 45, 3, 2, 3, 3, 4, 5]

Mahesh Karia
  • 2,045
  • 1
  • 12
  • 23
evan
  • 1
  • 1
  • Is it intended to remove the value entirely? Or do you just want to reduce it one instance. – RoadRunner Jan 02 '18 at 06:05
  • 1
    what's your expected output? Are you trying to remove ALL occurrences of `a` or just the repeated ones? e.g. if `lst = [1,2,3,4,5,5]` and you call `rem(5,lst)`, are you expecting the output `[1,2,3,4,5]` or `[1,2,3,4]`? – Swifty Jan 02 '18 at 06:07

5 Answers5

4

There are 2 mistakes in your code. The first one is

while a in li == True: In fact, this check always returns False since li == True is False.

It should actually be while (a in li) == True:, or while a in li:

Also, if you are trying to delete only repeated occurrences of a (i.e. leave the first occurrence of a in) then list comprehension won't suit your needs. You will have to add an additional check inside your rem() function that catches the first occurrence of a and then executes your loop:

def rem(a, li):
  list_length = len(li)
  i = 0
  while (li[i] != a) and (i < list_length):
    i += 1              # skip to the first occurrence of a in li
  i += 1                # increment i 
  while i < list_length:
    if li[i] == a:
      del li[i]
      print('Updated list: ', li)
      list_length -= 1          # decrement list length after removing occurrence of a
    else:
      i += 1

The code snippet above does not cover the edge cases where the list is empty, or the case a is not in the list. I'll leave those exercises to you.

Swifty
  • 839
  • 2
  • 15
  • 40
2

Try changing the while condition to while a in li

def rem(a,li):
    try:
        while a in li:
            li.remove(a)
        print('Updated list: ',li)
    except ValueError:
        print(a,' is not located in the list ',li)

L = [1,2,3,45,3,2,3,3,4,5]
rem(2,L)

In general, if you want to remove duplicates from a list then you can use the built-in set.

Van Peer
  • 2,127
  • 2
  • 25
  • 35
  • Thanks that works. does python interpret a in li as already containing the comparison of being equal to True? That is the only logic I can deduce from the code. – evan Jan 02 '18 at 06:09
  • @evan yes, you're right! there're better ways to do this as you can see from other answers. – Van Peer Jan 02 '18 at 06:10
  • 1
    What's the point of the try...except... ? You already have `while a in li`, therefore `a` has to be in `li` when `li.remove(a)` is called. When will a `ValueError` ever occur? (To be an acceptable answer, unnecessary code should be removed) – Taku Jan 02 '18 at 15:26
  • @abccd thanks for the feedback; this concept being quite new to me, will follow going forward. – Van Peer Jan 02 '18 at 19:53
2

Assuming you want to remove all instances of a from L, you could also just use a simple list comprehension:

def rem(a,li):
    return [x for x in li if x != a]

L = [1,2,3,45,3,2,3,3,4,5]
print(rem(2,L))

Which Outputs:

[1, 3, 45, 3, 3, 3, 4, 5]
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
2

That would be a better job for a list comprehension. L[:] = [a for a in L if a not in (2,)] Assigning to a slice will mutate the list in place.



I'm updating my answer to account for the various interpretations your question allows and also to make it more general by accepting also strings and multiple values to be removed at once.

def removed(items, original_list, only_duplicates=False, inplace=False):
    """By default removes given items from original_list and returns
    a new list. Optionally only removes duplicates of `items` or modifies
    given list in place.
    """
    if not hasattr(items, '__iter__') or isinstance(items, str):
        items = [items]

    if only_duplicates:
        result = []
        for item in original_list:
            if item not in items or item not in result:
                result.append(item)
    else:
        result = [item for item in original_list if item not in items]

    if inplace:
        original_list[:] = result
    else:
        return result

Docstring extension:

"""
Examples:
---------

    >>>li1 = [1, 2, 3, 4, 4, 5, 5]
    >>>removed(4, li1)
       [1, 2, 3, 5, 5]
    >>>removed((4,5), li1)
       [1, 2, 3]
    >>>removed((4,5), li1, only_duplicates=True)
       [1, 2, 3, 4, 5]

    # remove all duplicates by passing original_list also to `items`.:
    >>>removed(li1, li1, only_duplicates=True)
      [1, 2, 3, 4, 5]

    # inplace:
    >>>removed((4,5), li1, only_duplicates=True, inplace=True)
    >>>li1
        [1, 2, 3, 4, 5]

    >>>li2 =['abc', 'def', 'def', 'ghi', 'ghi']
    >>>removed(('def', 'ghi'), li2, only_duplicates=True, inplace=True)
    >>>li2
        ['abc', 'def', 'ghi']
"""

You should be clear about what you really want to do, modify an existing list, or make a new list with the specific items missing. It's important to make that distinction in case you have a second reference pointing to the existing list. If you have, for example...

li1 = [1, 2, 3, 4, 4, 5, 5]
li2 = li1
# then rebind li1 to the new list without the value 4
li1 = removed(4, li1)
# you end up with two separate lists where li2 is still pointing to the 
# original
li2
# [1, 2, 3, 4, 4, 5, 5]
li1
# [1, 2, 3, 5, 5]

This may or may not be the behaviour you want.

Darkonaut
  • 20,186
  • 7
  • 54
  • 65
  • It's not necessary if you don't care if a new list is created, but how the question is phrased I understand he want's to mutate the list, not replace it. You can check with `id(L)` that the list is still the same as before when you assign to a slice, while it gets a new id in case you skip the slice. – Darkonaut Jan 02 '18 at 15:54
1

Just keep track of index no and use del

Simple approach:

L = [1,2,3,45,3,2,3,3,4,5]

def rem(a,li):
    for j,i in enumerate(li):
        if a==i:
            del li[j]

    return li



print(rem(2,L))

output:

[1, 3, 45, 3, 3, 3, 4, 5]