delete items from a set while iterating over it

2019-02-11 16:16发布

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?

5条回答
太酷不给撩
2楼-- · 2019-02-11 16:23

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.

查看更多
等我变得足够好
3楼-- · 2019-02-11 16:27

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]
[]
查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-02-11 16:28

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))
查看更多
够拽才男人
5楼-- · 2019-02-11 16:30

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)
查看更多
叛逆
6楼-- · 2019-02-11 16:33

"Obviously, I cannot do it while still iterating over the original set."

I'm not sure that's true... I was expecting "concurrency" errors when I tried it but don't appear to have got any. For the record the code I'm using looks like this:

for member in myset:
    myset.remove( member )

("member" is a better choice of variable name for sets; "element" for lists)

Ah... just seen the comment by kindall under Zero P's answer: obviously kindall is an expert where I am a bumbler, but I'll post my answer anyway just to draw attention to the point...

NB Zero P regards kindall's assertion that this is OK with hostility... but given that sets are, by design, unordered, I think we can conclude that concurrency errors should (and do? in Python?) crop up only when deleting from an ordered collection (i.e. list - and even then you could use a reverse index countdown to avoid the problem).

It would therefore appear that aversion to deletion-while-iterating is a matter of superstition and/or hangovers from bad experiences from poorly implemented structures in other languages.

Final thought: sets and iterations don't really go together terribly well: since a set is unordered you could only ever be iterating in random fashion over a subset of all members (specifically the subset 'all' or 'itself'!). Sets are optimised for equality tests and duplicate removal, and this reflects their intended use.

So an equivalent bit of code to the above (the ideal solution?) is:

while myset:
    member = myset.pop()
    # do something
查看更多
登录 后发表回答