48

I have a set myset, and I have a function which iterates over it to perform some operation on its items and this operation ultimately deletes the item from the set.

Obviously, I cannot do it while still iterating over the original set. I can, however, do this:

mylist = list(myset)
for item in mylist:
    # do sth

Is there any better way?

skyork
  • 7,113
  • 18
  • 63
  • 103
  • 3
    Does the operation *always* delete the item, or only sometimes? If you always delete the item, it might be easier just to empty the set out after the iteration is complete. – Blckknght Dec 29 '15 at 20:32
  • ...and you can empty the set with `myset.clear()` – Robert Siemer Jan 07 '23 at 16:41

5 Answers5

30

First, using a set, as Zero Piraeus told us, you can

myset = set([3,4,5,6,2])
while myset:
    myset.pop()
    print(myset)

I added a print method giving these outputs

>>> 
set([3, 4, 5, 6])
set([4, 5, 6])
set([5, 6])
set([6])
set([])

If you want to stick to your choice for a list, I suggest you deep copy the list using a list comprehension, and loop over the copy, while removing items from original list. In my example, I make length of original list decrease at each loop.

l = list(myset)
l_copy = [x for x in l]
for k in l_copy:
    l = l[1:]
    print(l)

gives

>>> 
[3, 4, 5, 6]
[4, 5, 6]
[5, 6]
[6]
[]
phoenix
  • 7,988
  • 6
  • 39
  • 45
kiriloff
  • 25,609
  • 37
  • 148
  • 229
16

This ought to work:

while myset:
    item = myset.pop()
    # do something

Or, if you need to remove items conditionally:

def test(item):
    return item != "foo"  # or whatever

myset = set(filter(test, myset))
Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • 2
    this works well if I just want to delete everything from the set. In my case, I need to get hold of each item, and apply a function to it **before** removing it from the set. – skyork May 14 '13 at 19:53
  • 1
    @skyork removing items from iterables as you iterate over them is a bad idea (bad enough that Python will raise an exception if it works out that's what you're doing). You're better of building a new set and then throwing the old one away. – Zero Piraeus May 14 '13 at 19:58
  • 2
    "removing items from iterables as you iterate over them is a bad idea (bad enough that Python will raise an exception if it works out that's what you're doing" -- a common misconception, but untrue. For some iterables (hashtable-based ones specifically), it is not legal (note: not "a bad idea") and Python will inform you of that when you try. For other iterables, it is perfectly valid and, once you understand how it works, predictable. – kindall May 14 '13 at 20:45
  • @kindall "If the implementation is hard to explain, it's a bad idea" (also, "Readability counts" and "Beautiful is better than ugly"). Yes, for some iterables there are tricks like iterating in reverse. But just because you can, doesn't mean you should. – Zero Piraeus May 14 '13 at 21:24
  • 1
    @ZeroPiraeus There are better ways to do it (creating a new list is usually faster, if you have the memory) but that doesn't mean that doing it the other way is "a bad idea." – kindall May 14 '13 at 22:02
7

Let's return all even numbers while modifying current set.

myset = set(range(1,5))
myset = filter(lambda x:x%2==0, myset)
print myset

Will return

>>> [2, 4]

If there is opportunity use always use lambda it will make your life easier.

That_User
  • 929
  • 2
  • 7
  • 25
  • 7
    I don't agree that `lambda` always makes your life easier ... Actually, it seems that it usually _doesn't_ make your life easier . . . – mgilson Dec 29 '15 at 18:45
4

Another way could be :

s=set()
s.add(1)
s.add(2)
s.add(3)
s.add(4)
while len(s)>0:
    v=next(iter(s))
    s.remove(v)
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
Sanjeev Kumar
  • 112
  • 2
  • 3
2

Use the copy library to make a copy of the set, iterate over the copy and remove from the original one. In case you want to stick to the for loop and you want to iterate over one element only once - comes in handy if you don't necessarily want to remove all the elements.

import copy
for item in copy.copy(myset):
    myset.remove(item)
Viktor Tóth
  • 423
  • 6
  • 12
  • 3
    This is the real answer. You don't need to resort to `copy`, though. In Python `for item in set(myset): ...` creates a copy of the set. – ingyhere Sep 23 '21 at 01:51