views:

257

answers:

4

This is really only easy to explain with an example, so to remove the intersection of a list from within a dict I usually do something like this:

a = {1:'', 2:'', 3:'', 4:''}
exclusion = [3, 4, 5]

# have to build up a new list or the iteration breaks
toRemove = []
for var in a.iterkeys():
    if var in exclusion:
        toRemove.append(var)

for var in toRemove:
    del a[var]

This might seem like an unusual example, but it's surprising the number of times I've had to do something like this. Doing this with sets would be much nicer, but I clearly want to retain the 'values' for the dict.

This method is annoying because it requires two loops and an extra array. Is there a cleaner and more efficient way of doing this.

+10  A: 

Consider dict.pop:

for key in exclusion:
     a.pop(key, None)

The None keeps pop from raising an exception when key isn't a key.

Blair Conrad
Oh, this is far better than my answer.
SpoonMeiser
nice tip, dict.pop is oft-forgotten.
camflan
You can use any value instead of None: deleted = [d.pop(k,0) for k in exclusion]
J.F. Sebastian
It's true. None seemed cheap and easy and very... Noneish. I figured it would feel right in any circumstances, where 0 might feel weird in a string context or "" in a numeric one.
Blair Conrad
This is really nice. Much cleaner. I would question whether there would be a more efficient way if the list of exclusions was significantly larger than the dict, rather than iterating through the entire list?
Dan
+2  A: 

Why not just use the keys method, instead of iterkeys? That way you can do it in one loop because it returns a list, not an iterator.

SpoonMeiser
+3  A: 
a = dict((key,value) for (key,value) in a.iteritems() if key not in exclusion)
Dave Webb
This solution avoids explicit loops, which is good, but it reconstructs the dictionary from scratch, which is bad. Maybe dictionaries should steal some methods from sets?
Rafał Dowgird
I'm never sure how much benefit you get from avoid loops and so staying in the C code. I guess it will all depend on how big your dict is and how much of it you're removed. I think Blair's answer would be more efficient for large dicts but I've have to test it to be sure.
Dave Webb
Funny, removing half of the keys from a 200k dict (a[i]=i for i in range(200k)) in a loop is only about 30% slower on my machine than copying the dict.
Rafał Dowgird
+1  A: 

You could change your exclusion list to a set, then just use intersection to get the overlap.

exclusion = set([3, 4, 5])

for key in exclusion.intersection(a):
    del a[key]
Brian