user11846775
user11846775

Reputation:

How to remove first occurrence of a specific item from a list of items without using .pop() or .remove()

I have a list, let us call it l = [1,2,3,7,8,9,10,7]. Given this list l, I am trying to remove the first occurrence of the number 7 without using the .pop() or .remove() built-in functions.

I have tried

def remove_item(l, item_to_remove):
  newlst = []
  for item in l:
      if item != item_to_remove:
        newlst.append(item)
  return newlst

However, this removes all instances of the item I am trying to remove when in fact I only want to remove the very first instance of that said specific item. Does anyone have some tips on how to accomplish this??

Upvotes: 3

Views: 4491

Answers (7)

Alexander
Alexander

Reputation: 109556

.index(x) returns the first index location of x within the list, so just delete it. If x is not found, it returns ValueError.

my_list = [1, 2, 3, 7, 8, 9, 10, 7]
val = 7

if val in my_list:
    del my_list[my_list.index(val)]

>>> my_list
[1, 2, 3, 8, 9, 10, 7]

Signature: my_list.index(value, start=0, stop=9223372036854775807, /)

Docstring: Return first index of value.

Raises ValueError if the value is not present.

Upvotes: 2

Larry
Larry

Reputation: 487

Very wasteful, but here you go, a solution:

def remove_first(sequence, element):
    return sequence[:sequence.index(element)] + sequence[sequence.index(element)+1:]

Then you can:

>>> remove_first(["a", "b", "a", "c"], "a"):
['b', 'a', 'c']

index returns the index of the first found occurrence of an element. The rest is sequence splicing and catenation.

Of course, you could generalize this to remove(sequence, element, n) to remove the n-th found element. EDIT: I just stated falsely that index also supports that. Statement removed.

Or you could choose to mutate the input, but for one, returning the output is cleaner, and you could not have a general "sequence" argument, as not all sequences are mutable. See the tuple type.

Upvotes: 2

ShadowRanger
ShadowRanger

Reputation: 155363

Similar idea to CEWeinhauer's solution, but one which takes advantage of Python features to minimize overhead once we've found the item to remove:

def remove_item(l, item_to_remove):
    newlst = []
    liter = iter(l)                 # Make single pass iterator, producing each item once
    for item in liter:
        if item == item_to_remove:  # Found single item to remove, we're done
            break
        newlst.append(item)         # Not found yet
    newlst += liter                 # Quickly consume all elements after removed item without tests
    return newlst

The above works with any input iterable in a single pass, so it's better if the input might not be a list and/or might be huge. But it's admittedly more complex code. The much simpler solution is to just find the element with index and remove it. It might be slightly slower in some cases, since it's two O(n) steps instead of just one, but it uses C built-ins more, so it's likely to be faster in practice:

 def remove_item(l, item_to_remove):
     newlst = list(l)
     del newlst[newlst.index(item_to_remove)]
     return newlst

Upvotes: 1

Viach
Viach

Reputation: 508

lst = [1,2,3,7,8,9,10,7]
new_lst = lst[:lst.index(7)] + lst[lst.index(7) + 1:]
new_lst

[1, 2, 3, 8, 9, 10, 7]

Upvotes: 1

Frank
Frank

Reputation: 2029

You only need to take care that the removing part of your code doesn't run twice.

lst = [1,2,3,7,8,9,10,7]  # [1, 2, 3, 7, 8, 9, 10, 7]
print(lst)
for i in range(len(lst)):
    if lst[i] == 7:
        del lst[i]  # [1, 2, 3, 8, 9, 10, 7]
        break
print(lst)

It does exactly the same as the following:

lst = [1,2,3,7,8,9,10,7]
print(lst)  # [1, 2, 3, 7, 8, 9, 10, 7]
for i in range(len(lst)):
    if lst[i] == 7:
        lst.pop(i)
        break
print(lst)  # [1, 2, 3, 8, 9, 10, 7]

as well as this

lst = [1,2,3,7,8,9,10,7]
print(lst)  # [1, 2, 3, 7, 8, 9, 10, 7]
for i in range(len(lst)):
    if lst[i] == 7:
        lst.remove(lst[i])
        break
print(lst)  # [1, 2, 3, 8, 9, 10, 7]

Overview of the used methods:

  1. del list[i] - The del statement can also be used to remove slices from a list
  2. list.pop - remove and return item at index (default last). Raises IndexError if list is empty or index is out of range.
  3. list.remove - remove first occurrence of value.Raises ValueError if the value is not present.

Upvotes: 5

sam
sam

Reputation: 1896

Welcome to StackOverflow!

Minor modification to your code,.

I would prefer remove but here is your modified code to do the required job

def remove_item(l, item_to_remove):
  newlst = []
  for item in l:
      if item != item_to_remove:
        newlst.append(item)
      else:
        return newlst + l[len(newlst) + 1 :]
  return newlst

In Python, you can add the lists. Using list comprehensions, you select sub-lists(l[len(newlst) + 1 :]).

Testing

>>> list = [1,3,4,5,6,7,3,10]
>>> print(remove_item(list, 3))
[1, 4, 5, 6, 7, 3, 10]

Upvotes: 1

CEWeinhauer
CEWeinhauer

Reputation: 123

You just need to add a little logic to it. I add a looking variable which signifies that we havent found the entry were looking for. Heres the code

def remove_item(l, item_to_remove):
newlst = []
looking = True
for item in l:
    if item != item_to_remove or not looking:
        newlst.append(item)
    else:
        looking = False

return newlst

list = [1,3,4,5,6,7,3,10]
print(remove_item(list, 3))

which returns [1, 4, 5, 6, 7, 3, 10]

Upvotes: 2

Related Questions