Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to iterate on a changing dictionary

40 views
Skip to first unread message

TheSaint

unread,
Jun 19, 2011, 10:32:36 AM6/19/11
to
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?
--
goto /dev/null

Chris Angelico

unread,
Jun 19, 2011, 11:13:58 AM6/19/11
to pytho...@python.org
On Mon, Jun 20, 2011 at 12:32 AM, TheSaint <nob...@nowhere.net.no> wrote:
> 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

Roy Smith

unread,
Jun 19, 2011, 11:53:44 AM6/19/11
to
In article <mailman.154.1308496...@python.org>,
Chris Angelico <ros...@gmail.com> wrote:

> On Mon, Jun 20, 2011 at 12:32 AM, TheSaint <nob...@nowhere.net.no> wrote:
> > 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.

Terry Reedy

unread,
Jun 19, 2011, 12:02:12 PM6/19/11
to pytho...@python.org
On 6/19/2011 11:13 AM, Chris Angelico wrote:
> On Mon, Jun 20, 2011 at 12:32 AM, TheSaint<nob...@nowhere.net.no> wrote:
>> 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.

> 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.

--
Terry Jan Reedy

Terry Reedy

unread,
Jun 19, 2011, 12:51:06 PM6/19/11
to pytho...@python.org
On 6/19/2011 11:53 AM, Roy Smith wrote:

> 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}

--
Terry Jan Reedy

Lie Ryan

unread,
Jun 19, 2011, 1:00:27 PM6/19/11
to
On 06/20/11 00:32, TheSaint wrote:
> 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.

TheSaint

unread,
Jun 20, 2011, 9:20:55 AM6/20/11
to
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?

--
goto /dev/null

Florencio Cano

unread,
Jun 20, 2011, 10:30:52 AM6/20/11
to TheSaint, pytho...@python.org
> 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.

Terry Reedy

unread,
Jun 20, 2011, 1:37:56 PM6/20/11
to pytho...@python.org
On 6/20/2011 10:30 AM, Florencio Cano wrote:
>> 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.

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.

--
Terry Jan Reedy

TheSaint

unread,
Jun 21, 2011, 6:44:02 AM6/21/11
to
Terry Reedy wrote:

> 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.

--
goto /dev/null

Gurpreet Singh

unread,
Jun 21, 2011, 7:51:14 AM6/21/11
to pytho...@python.org
Perhaps this is the simplest and best solution which appears in this case. Just copy the desired items to a new dictionary and discard the original one.

import re
myDict={'a':'alpha','b':'beta','c':'charley','d':'disney'}
myNewDict={}
for k,v in myDict.iteritems():
if re.search("a",v)!=None:
myNewDict[k]=v
print myDict
print myNewDict

Gurpreet Singh

unread,
Jun 21, 2011, 7:51:14 AM6/21/11
to comp.lan...@googlegroups.com, pytho...@python.org

MRAB

unread,
Jun 21, 2011, 10:15:13 AM6/21/11
to pytho...@python.org

Using regex is overkill. Try this instead:

if "a" in v:

0 new messages