Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python iterating over dictionaries

Tags:

python

I have two dictionaries.I'm iterating over it one inside the other. I want to select the next item each time it iterates inside the inner loop rather than from the beginning.

dict1={'a':1 , 'b':2 , 'c':3}
dict2={'x':10, 'y':20, 'z':30}

for key,value in dict1:
   #do something
    for k,v in dict2:
          #do something

When key='a', it iterates over inner loop and does some action according to the code in that loop.Suppose that it action that it selected was 'x' in dict2. Now when I have to iterate with key='b', I want the iteration for the inner loop to start from 'y' because 'x' was already selected.

like image 911
Mark Avatar asked Jan 23 '14 21:01

Mark


1 Answers

First, for key,value in dict1 doesn't do what you want—it just iterates over the keys. If you want to iterate over key-value pair, you need for key, value in dict1.items().

Meanwhile, it sounds like what you're asking for is lock-step iteration, not nested iteration? If so, you want the zip function:

for (k1, v1), (k2, v2) in zip(dict1.items(), dict2.items()):
    # do something

But note that the ordering this gives you will actually be arbitrary, because the ordering of dicts is inherently arbitrary. So, if you're happy with ('a', 'x'), then ('b', 'y'), etc., and also happy with ('a', 'y'), then ('b', 'x'), then zip is what you want. If not, it isn't.

(If you expected your dicts to have some inherent order, such as order of insertion, or sorted order, or anything else, you will have to use a different class. For example, collections.OrderedDict acts like a dictionary, but maintains the insertion order.)


If you want something more complicated, where you move the iteration over dict2 programmatically in a way you can't express in a one-liner, you can always use the iteration protocol manually:

iter2 = iter(dict2.items())
for k1, v1 in dict1.items():
    k2, v2 = next(iter2)
    # do something
    # maybe do another k2, v2 = next(iter2)
    # maybe restart iter2 = iter(dict2.items())
    # and so on

As the docs explain, when next(iter2) gets to the end, it will raise StopIteration. What if you don't want that? Maybe you want to, as you suggested in a comment, start over again? You can solve that by catching StopIteration, or by using the two-argument form of next and checking for a sentinel. For example:

try:
    k2, v2 = next(iter2)
except StopIteration:
    iter2 = iter(dict2.items())
    k2, v2 = next(iter2)

Or you can even write a circular iteration wrapper:

def circular_iterate(iterable_factory):
    while True:
        yield from iterable_factory():

In Python 3.2 or earlier, you have to replace the yield from with an inner loop:

def circular_iterate(iterable_factory):
    while True:
        for value in iterable_factory():
            yield value

And now:

iter2 = circular_iterate(dict2.items) # note, not dict2.items()

If you're trying to track more than just a "current position", you probably need an additional data structure. For example, if you want to skip over all keys seen so far, use a set of keys seen so far, or a set of keys not seen:

seen_items_2 = set()
for k1, v1 in dict1.items():
    for k2, v2 in dict2.items():
        if k2 in seen_items_2:
            continue
        seen_items_2.add(k2)
        # do something
like image 185
abarnert Avatar answered Oct 05 '22 14:10

abarnert