MikG
MikG

Reputation: 1019

iterating over values list in Python dictionary

Hi I am looking to iterate over a Python dictionary where each key has a list of values, I am looking to either create a new list/dictionary where I have separated each value[x] or directly do stuff with the separated values.

here's a simplified example of the dictionary I have:

all_parameters = {"first": ["1a","1b","1c"], "second": ["2a","2b","2c"], "third": ["3a","3b","3c"]}

I am looking to separate the values like this (either by creating a new dictionary or list or directly doing stuff with the separated values).

grouped_parameters = [{"first": "1a", "second": "2a", "third": "3a"},
                      {"first": "1b", "second": "2b", "third": "3b"},
                      {"first": "1c", "second": "2c", "third": "3c"}]

I am insure how to iterate correctly over each key:value pair.

i = 0
for k, v in all_parameters.items():
    for item in v:
        # stuck here
    i+=1

Eventually the 'stuff' I am looking to do is convert each output (e.g. '{"first": "1a", "second": "2a", "third": "3a"}') into a string so that I can post each parameter group to a cell in a table, so ideally i'd prefer to do this dynamically instead of creating a new dictionary.

Any help would be greatly appreciated.

Upvotes: 3

Views: 2667

Answers (4)

PM 2Ring
PM 2Ring

Reputation: 55469

The items in a plain dict aren't ordered *, so you need to be careful when converting a dict to a string if you want the fields to be in a certain order.

This code uses a tuple containing the key strings in the order we want them to be in in the output dict strings.

all_parameters = {
    "first": ["1a","1b","1c"], 
    "second": ["2a","2b","2c"], 
    "third": ["3a","3b","3c"],
}

# We want keys to be in this order
all_keys = ("first", "second", "third")

# Assumes all value lists are the same length.
for t in zip(*(all_parameters[k] for k in all_keys)):
    a = ['"{}": "{}"'.format(u, v) for u, v in zip(all_keys, t)]
    print('{' + ', '.join(a) + '}')

output

{"first": "1a", "second": "2a", "third": "3a"}
{"first": "1b", "second": "2b", "third": "3b"}
{"first": "1c", "second": "2c", "third": "3c"}

How it works

We first use a generator expression (all_parameters[k] for k in all_keys) which yields the value lists from all_parameters in the order specified by all_keys. We pass those lists as args to zip using the * "splat" operator. So for your example data, it's equivalent to calling zip like this:

zip(["1a","1b","1c"], ["2a","2b","2c"], ["3a","3b","3c"])

zip effectively transposes the iterables you pass it, so the result of that call is an iterator that produces these tuples:

('1a', '2a', '3a'), ('1b', '2b', '3b'), ('1c', '2c', '3c')

We then loop over those tuples one by one, with the for t in zip(...), so on the first loop t gets the value ('1a', '2a', '3a'), then ('1b', '2b', '3b'), etc.

Next we have a list comprehension that zips the value strings up with the corresponding key string and formats them into a string with double-quotes around each key and value string. We then join those strings together with commas and spaces as separators (and add brace characters) to make our final dict strings.


* Actually in Python 3.6 plain dicts do retain insertion order, but that is currently an implementation detail, and it should not be relied upon.

Upvotes: 1

asongtoruin
asongtoruin

Reputation: 10359

If there's a chance of the lists being of different length, you could use map with None like so:

all_parameters = {"first": ["1a", "1b", "1c", "1d"], "second": ["2a", "2b", "2c"], "third": ["3a", "3b", "3c"]}

final = [dict(zip(all_parameters.keys(), values)) for values in map(None, *all_parameters.values())]

print final

map(None, *all_parameters.values()) gives you a tuple of the values for each key at each index - e.g. ('1a', '2a', '3a'), and by zipping this to the keys and creating a dictionary, we get the required combination.

Note: this will only work in Python 2.x as map changed in 3.x. For Python 3.x we can use itertools.zip_longest:

from itertools import zip_longest

all_parameters = {"first": ["1a", "1b", "1c", "1d"], "second": ["2a", "2b", "2c"], "third": ["3a", "3b", "3c"]}

final = [dict(zip(all_parameters.keys(), values)) for values in zip_longest(*all_parameters.values())]

print(final)

In both cases we get:

[{'second': '2a', 'third': '3a', 'first': '1a'}, {'second': '2b', 'third': '3b', 'first': '1b'}, {'second': '2c', 'third': '3c', 'first': '1c'}, {'second': None, 'third': None, 'first': '1d'}]

Upvotes: 1

timgeb
timgeb

Reputation: 78670

Assuming all lists have the same length:

>>> length = len(next(all_parameters.itervalues()))
>>> [{k:v[i] for k,v in all_parameters.iteritems()} for i in range(length)]
[{'second': '2a', 'third': '3a', 'first': '1a'}, {'second': '2b', 'third': '3b', 'first': '1b'}, {'second': '2c', 'third': '3c', 'first': '1c'}]

In Python 3, use len(next(iter(all_parameters.values()))) and items instead of iteritems.

(The iterator shenanigans are done because you don't need a list of all the dictionary values if you only want the length of an arbitrary value-list.)

Upvotes: 3

OPhamster
OPhamster

Reputation: 11

I'm not sure if this is what you want but

for i in range(len(all_parameters['first'])):
     for position in all_parameters:
         print(position+" "+all_parameters[position][i])

gives an output like this

first 1a second 2a third 3a first 1b second 2b third 3b first 1c second 2c third 3c

This will work only if each dictionary element has a list the same size as the first one.

Upvotes: 0

Related Questions