-1

Let's say I have a dictionary of lists. Example case:

{"move1": ["somedata1", "somedata2", "somedata3"],
 "move2": ["somedata4", "somedata5"],
 "move":  ["somedata6"]}

And when I iterate over it I want to have a result that looks like this:

Iteration 1:

{"move1": ["somedata1"],
 "move2": ["somedata4"],
 "move3": ["somedata6"]}

Iteration 2:

{"move1": ["somedata2"],
 "move2": ["somedata5"]}

Iteration 3:

{"move1": ["somedata3"]}

How can I do that in a way that is both efficient and pretty easy to understand.

Note that the move labels don't have to be removed, they can be empty lists in case that makes the solution easier. Example in iteration 3:

{"move1": ["somedata3"]
 "move2": [], 
 "move3": []}
Royi Levy
  • 513
  • 1
  • 3
  • 14
  • 2
    Where is your attempt of a solution, and how does it fail? – guidot Feb 01 '21 at 09:56
  • So your question is how to iterate a dictionary? `for k,v in d.items()`? – Tomerikoo Feb 01 '21 at 10:01
  • @guidot I don't really know how to even approach this... I will probably come up with something that is overcomplicated. – Royi Levy Feb 01 '21 at 10:02
  • It seems that you want to iterate something by order/indexes. dict is probably less adequate for that purpose – Tomerikoo Feb 01 '21 at 10:03
  • @Tomerikoo No, how to iterate dictionaries inside a dictionary, basically at the same time regardless if one is longer than the others. – Royi Levy Feb 01 '21 at 10:05
  • Please post example data that is actually runnable. – juanpa.arrivillaga Feb 01 '21 at 10:07
  • @juanpa.arrivillaga In reality the values of those inner dictionaries are of a complicated class, to make it easier I changed them to string. – Royi Levy Feb 01 '21 at 10:12
  • Does this answer your question? [How to iterate over two dictionaries of different lengths?](https://stackoverflow.com/questions/64939285/how-to-iterate-over-two-dictionaries-of-different-lengths) – Wimanicesir Feb 01 '21 at 10:17
  • Does this answer your question? [How to pretty print nested dictionaries?](https://stackoverflow.com/questions/3229419/how-to-pretty-print-nested-dictionaries) – guidot Feb 01 '21 at 10:24

3 Answers3

3

As always, when you want to iterate over iterables taking the values in the same position, but the iterables are of different lengths, use zip_longest. This will have an automatic fill-value of None.

In [1]: from itertools import zip_longest

In [2]: data = {"move1": {"recording1": "somedata1", "recording2": "somedata2", "recording3": "somedata3"},
   ...:  "move2": {"recordingA": "somedata4", "recordingB": "somedata5"},
   ...:  "move3": {"recordingI": "somedata6"}}

In [3]: for group in zip_longest(*map(dict.items, data.values()), fillvalue=None):
   ...:     result = {k:{} if v is None else dict([v]) for k,v in zip(data, group)}
   ...:     print("="*100)
   ...:     print(result)
   ...:
====================================================================================================
{'move1': {'recording1': 'somedata1'}, 'move2': {'recordingA': 'somedata4'}, 'move3': {'recordingI': 'somedata6'}}
====================================================================================================
{'move1': {'recording2': 'somedata2'}, 'move2': {'recordingB': 'somedata5'}, 'move3': {}}
====================================================================================================
{'move1': {'recording3': 'somedata3'}, 'move2': {}, 'move3': {}}

I reconstituted the dicts you wanted into result, although, that honestly doesn't make a lot of sense. You should probably just work with what the zip_longest iterator is providing you. But at least this demonstrates the basic idea.

Edit

If you are working with lists, you can do something even more simple. Something like:
In [6]: data = {"move1": ["somedata1", "somedata2", "somedata3"],
   ...:  "move2": ["somedata4", "somedata5"],
   ...:  "move":  ["somedata6"]}
In [7]: for group in zip_longest(*data.values()):
   ...:     print("="*100)
   ...:     result = dict(zip(data, group))
   ...:     print(result)
   ...:
====================================================================================================
{'move1': 'somedata1', 'move2': 'somedata4', 'move': 'somedata6'}
====================================================================================================
{'move1': 'somedata2', 'move2': 'somedata5', 'move': None}
====================================================================================================
{'move1': 'somedata3', 'move2': None, 'move': None}

I didn't convert the values of the results to single-element / empty lists because that doesn't really make a lot of sense. If you really need that, you should be able to take it from here.

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
1

Use two loops. The outer loop will take care of position in the list, and the inner one will iterate through the_dictionary

max_list_len = max(len(x) for x in the_dictionary.values())

for i in range(max_list_len):
    for key, value in the_dictionary.items():
        print(key, value[i] if i < len(value) else [])

P.S. I've just printed to the console. You can rewrite to make it into another list / dictionary as you need

Eeshaan
  • 1,557
  • 1
  • 10
  • 22
1

I noticed that since the first post the question changed a couple of times. So below is one of the implementations for one of your questions.

Here we create a generator function yielding the required dictionaries. Inside the while loop we pop values (until the inner dicts are empty) and construct the new dictionary. Once the resulting dictionary is empty, we terminate the loop.

Since your question changed, this probably does not solve your problem exactly, but you may get the idea.

from contextlib import suppress

data = {
    "move1": {
        "recording1": "a",
        "recording2": "b",
        "recording3": "c",
    },
    "move2": {"recordingA": "d", "recordingB": "e"},
    "move3": {"recordingI": "f"},
}


def _iterate(input_data):
    data = input_data.copy()
    while True:
        result = {}
        for key, inner_dict in data.items():
            with suppress(StopIteration):
                first_key = next(iter(inner_dict))
                result[key] = {first_key: data[key].pop(first_key)}
        if not result:
            return
        yield result


for item in _iterate(data):
    print(item)

Output

{'move1': {'recording1': 'a'}, 'move2': {'recordingA': 'd'}, 'move3': {'recordingI': 'f'}}
{'move1': {'recording2': 'b'}, 'move2': {'recordingB': 'e'}}
{'move1': {'recording3': 'c'}}
Maxim Ivanov
  • 299
  • 1
  • 6