How to iterate on a changing dictionary

T

TheSaint

Hello

Trying to pop some key from a dict while is iterating over it will cause an
exception.
How I can remove items when the search result is true.

Example:

while len(dict):
for key in dict.keys():
if dict[key] is not my_result:
dict.pop(key)
else:
condition_to_break
print('Dictionary is over')

this is my mistake, but where to fix?
 
C

Chris Angelico

Hello

Trying to pop some key from a dict while is iterating over it will cause an
exception.
How I can remove items when the search result is true.

Example:

while len(dict):
  for key in dict.keys():
     if dict[key] is not my_result:
        dict.pop(key)
   else:
      condition_to_break
print('Dictionary is over')

One way is to iterate over an explicitly formed list of the keys.

for key in list(dict.keys()):

That creates an entirely new list with a snapshot copy of the keys. If
you then remove elements from the dictionary, the list will still
iterate correctly.

I'm not sure what you're trying to do, but you may find it easier to
use the 'filter' function (which takes an iterable, so possibly use
dict.iteritems() for that).It'll keep some and not others, and then
you can make use of just the ones you get back.

Chris Angelico
 
R

Roy Smith

Chris Angelico said:
Hello

Trying to pop some key from a dict while is iterating over it will cause an
exception.
How I can remove items when the search result is true.

Example:

while len(dict):
  for key in dict.keys():
     if dict[key] is not my_result:
        dict.pop(key)
   else:
      condition_to_break
print('Dictionary is over')

One way is to iterate over an explicitly formed list of the keys.

for key in list(dict.keys()):

That creates an entirely new list with a snapshot copy of the keys. If
you then remove elements from the dictionary, the list will still
iterate correctly.

If the dict is large and you only want to remove a relatively small
number of keys, you could instead build up a list of keys to be deleted
and do them in a second pass:

# pseudo-code
pending_keys = []
for key in dict.keys():
if dict[key] is not my_result:
pending_keys.append(key)
for key in pending_keys:
del dict[key]

Yet another variation which makes sense if you want to delete most of
the keys would be to copy them to a new dictionary. I'm not sure how
Python handles memory management on dictionaries which shrink. I'm
guessing it doesn't do anything about resizing the hash table downwards,
so you end up with a lot of wasted space. This solves that problem:

# pseudo-code
new_dict = {}
for key in dict.keys():
if dict[key] is my_result:
new_dict[key] = dict[key]

although you could improve on that with iteritems() instead of keys().

Of course, of all these, Chris Angelico's original code is the most
straight-forward, and that's would I would use unless you had some
strong reason to believe performance was an issue.
 
T

Terry Reedy

Hello

Trying to pop some key from a dict while is iterating over it will cause an
exception.
How I can remove items when the search result is true.

Example:

while len(dict):
for key in dict.keys():
if dict[key] is not my_result:
dict.pop(key)
else:
condition_to_break
print('Dictionary is over')

One way is to iterate over an explicitly formed list of the keys.

for key in list(dict.keys()):

That creates an entirely new list with a snapshot copy of the keys. If
you then remove elements from the dictionary, the list will still
iterate correctly.

The other is to make a set of to_be_deleted keys and delete them all
when done.

If you only want to delete one key, break the iteration and then delete.
 
T

Terry Reedy

Yet another variation which makes sense if you want to delete most of
the keys would be to copy them to a new dictionary. I'm not sure how
Python handles memory management on dictionaries which shrink.

'Python' does not handle memory management; each implementation does -).
I believe CPython will shrink lists and dicts that are too empty, but it
should be both more efficient and more dependable to make a new one as
with your code or

old_d = {i:i for i in range(100)}
d = {key:val for key,val in old_d.items() if not key%13}
d{0: 0, 65: 65, 39: 39, 13: 13, 78: 78, 52: 52, 26: 26, 91: 91}
 
L

Lie Ryan

Hello

Trying to pop some key from a dict while is iterating over it will cause an
exception.
How I can remove items when the search result is true.

Example:

while len(dict):
for key in dict.keys():
if dict[key] is not my_result:
dict.pop(key)
else:
condition_to_break
print('Dictionary is over')


Others has described how to do what you wanted to do, but let's address
the main problem here, why are you iterating a dictionary?

I found that most of the time that I thought I needed to iterate through
a dictionary, it's really because I'm thinking the wrong way, and a
little bit more thought lead me to a better way that doesn't involve
iterating on the dictionary.

While there are legitimate reasons for iterating a dictionary, I'd
consider the alternatives first.
 
T

TheSaint

Lie Ryan wrote:

Thank you all for the information, really apreciated.
While there are legitimate reasons for iterating a dictionary, I'd
consider the alternatives first.

Perhaps the correct answer is in what you said.

For certain reasons, searching in a dictionary is the fastest method,
secondly the positions of the data aren't relevant and easy to find.

My primer purpose is to know how much of work is done as soon the child
process reports completion of a part. The order of the given jobs are not
linear as it could do with a list.

To make an example: imaging Bingo.Shuffle the numbers, each number sorted
should be removed from the container, how would it implemented?
 
F

Florencio Cano

To make an example: imaging Bingo.Shuffle the numbers, each number sorted
should be removed from the container, how would it implemented?

The structure seems a set -> unordered collection of unique elements.

You can select a random element from the set with
random.sample(container, num_of_elems)

And then remove the elem from the set with remove.
 
T

Terry Reedy

The structure seems a set -> unordered collection of unique elements.

You can select a random element from the set with
random.sample(container, num_of_elems)

And then remove the elem from the set with remove.

random.sample(someset) just turns the set into a sequence (tuple).
For bingo, where you will play multiple games,
start with an immutable collection balls = ('A1', 'A2', ...)
that you cannot accidentally modify and can repeatedly copy.
For each game, copy to a list game = list(balls).
Then random.shuffle(game), and game.pop() balls as needed.

Other situations will need other solutions.
 
T

TheSaint

Terry said:
Other situations will need other solutions.
Like a job's completion list.

Some number of workers get a job, and by time the caller sould know who and
what has finished. Then a dictionary would hold number of remaining jobs.
Similar a downloading list.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top