2
\$\begingroup\$

I would like to transform a dict of one format into a dict of another format.

The raw_input dict will have the shape:

'key': ['list of strings']

I would like to reformat it into a list of dicts with the following shape:

[
    {'key': 'string'},
    {'key': 'string'},
    ...etc for each item in the list of strings
]

My implementation is functional but naive, using a doubly nested for loop:

raw_input = {
  'error': ['string 1', 'string2'],
  'error2': ['string 3']
}

def return_dict_as_list(raw_input):
    mylist = []
    for foo in raw_input: 
        for bar in raw_input[foo]: 
            mylist.append( { 'mykey': foo, 'myvalue': bar } )
    return mylist

The output is as expected but I can't help but think there's a better way.

I did try a couple of list comprehensions, but the output was not what was desired:

for example this:

elist = [[{'field': item, 'message': msg} for msg in raw[item]] for item in raw]

returns nested lists within a list, which I could unpack but doesn't seem very zen.

here's an online repl with the code: https://repl.it/repls/UnpleasantBiodegradableSystems

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Also, you should make the purpose of your code the title of your question, not what you want out of a review. Have a look at How to Ask. \$\endgroup\$
    – Graipher
    Commented Jan 28, 2019 at 16:10

1 Answer 1

3
\$\begingroup\$

You can at least turn the inner for loop into a list/generator comprehension and use list.extend ("Flat is better than nested"):

def return_dict_as_list(raw_input):
    mylist = []
    for key, values in raw_input.items():
        my_list.extend({'mykey': key, 'myvalue': value} for value in values)
    return mylist

But you can even turn it into one list comprehension with two for loops in it (and no nested lists):

def return_dict_as_list(raw_input):
    return [{'mykey': key, 'myvalue': value}
            for key in raw_input
            for value in raw_input[key]]

This will be a bit faster, too, since list comprehensions loop at C speed.

Another approach is to make it a generator:

def to_list_generator(d):
    for key, values in d.items():
        for value in values:
            yield {'mykey': key, 'myvalue': value}

This makes it a lot easier to see what is happening (depending on your taste). It also means that you don't need to hold the new list in memory but can process one item at a time in some other function.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks @Graipher - if you don't mind, could you help me understand why these options might be considered more Pythonic? I am a beginner with Python and am seeking to increase my understanding. \$\endgroup\$
    – tim
    Commented Jan 28, 2019 at 16:32
  • 2
    \$\begingroup\$ @tim: All of them would be more Pythonic because they have better naming. The first one is only slightly better, because it avoids one nested level ("Flat is better than nested"), which is why the second one is even better (plus, list comprehensions are usually a bit faster than plain for loops). The third one might be considered more Pythonic because it is very clear what is happening and generators are very nice in general because they allow you to setup a pipeline of things being done with some data without having to create temporary lists for every step. \$\endgroup\$
    – Graipher
    Commented Jan 28, 2019 at 16:35
  • 1
    \$\begingroup\$ @tim: Edited the answer to include those points. \$\endgroup\$
    – Graipher
    Commented Jan 28, 2019 at 16:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.