简体   繁体   中英

Is there an easier way of transforming these python objects

I`d like to transform the following python input:

[{'paramA': ['valA1','valA2','valA3','valA4']},
 {'paramB': ['valB1','valB2','valB3','valB4']},
 {'paramC': ['valC1','valC2','valC3','valC4']},
 # ...........................................,
 {'paramN': ['valN1', 'valN2','valN3','valN4']}]

To the following output:

[{'paramA': 'valA1','paramB': 'valB1', 'paramC': 'valC1', ...,'paramN':'valN1' }
 {'paramA': 'valA2','paramB': 'valB2', 'paramC': 'valC2', ...,'paramN':'valN2' }
 {'paramA': 'valA3','paramB': 'valB3', 'paramC': 'valC3', ...,'paramN':'valN3' }
 {'paramA': 'valA4','paramB': 'valB4', 'paramC': 'valC4', ...,'paramN':'valN4' } ]

The only function I could think of is the following:

def transformParams( inputParams ):
    res = []
    for i in inputParams:
        for k,v in i.items():
            for ind, inv in enumerate(v):
                if len(res) <= ind:
                    res.append({})
                res[ind][k] = inv
    return res

I am new to Python and I think this can be done in an easier Pythonic way using list comprehensions. Can anyone please come with a better answer?

First join everything in the input list into one dictionary; that way you can use zip() to transform all values into rows, and re-combine that with the keys:

try:
   # use efficient Python 3 version in Python 2
   from future_builtins import zip
except ImportError:
   # Python 3
   pass

combined = reduce(lambda d1, d2: dict(d1, **d2), inputlist)
result = [dict(zip(combined, col)) for col in zip(*combined.values())]

The zip() function pairs up elements from the input lists together, producing a tuple with all first elements, then all second, etc. The * in a zip(*list_of_lists) call applies all contained sequences (here all lists from the combined dictionary values) as separate arguments, which zip() then proceeds to pair up. In essence this transposes the rows to column sequences.

Those column sequences are then re-combined with the keys (again using zip() to do the pairing) to form the output dictionaries.

Demo:

>>> inputlist = [
...     {'paramA': ['valA1','valA2','valA3','valA4']},
...     {'paramB': ['valB1','valB2','valB3','valB4']},
...     {'paramC': ['valC1','valC2','valC3','valC4']},
...     # ...........................................,
...     {'paramN': ['valN1', 'valN2','valN3','valN4']}]
>>> combined = reduce(lambda d1, d2: dict(d1, **d2), inputlist)
>>> [dict(zip(combined, col)) for col in zip(*combined.values())]
[{'paramN': 'valN1', 'paramC': 'valC1', 'paramB': 'valB1', 'paramA': 'valA1'}, {'paramN': 'valN2', 'paramC': 'valC2', 'paramB': 'valB2', 'paramA': 'valA2'}, {'paramN': 'valN3', 'paramC': 'valC3', 'paramB': 'valB3', 'paramA': 'valA3'}, {'paramN': 'valN4', 'paramC': 'valC4', 'paramB': 'valB4', 'paramA': 'valA4'}]
>>> from pprint import pprint
>>> pprint(_)
[{'paramA': 'valA1', 'paramB': 'valB1', 'paramC': 'valC1', 'paramN': 'valN1'},
 {'paramA': 'valA2', 'paramB': 'valB2', 'paramC': 'valC2', 'paramN': 'valN2'},
 {'paramA': 'valA3', 'paramB': 'valB3', 'paramC': 'valC3', 'paramN': 'valN3'},
 {'paramA': 'valA4', 'paramB': 'valB4', 'paramC': 'valC4', 'paramN': 'valN4'}]
l = [
    {"paramA": ["valA1","valA2","valA3","valA4"]},
    {"paramB": ["valB1","valB2","valB3","valB4"]},
    {"paramC": ["valC1","valC2","valC3","valC4"]},
]

output = [dict([(line.keys()[0], line.values()[0][index]) for line in l]) for index in range(len(l[0].values()[0]))]

print(output)

This is what I came up with. Not very pretty but gets the job done. Also, this requires that the length of the values is always the same and that the first key and value of the dictionary are the wanted param and val.

Edit: Ignore this answer. Martijn Pieters answer is way more beautiful and readable!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM