3

How do I CONSOLIDATE the following using python COMPREHENSION

FROM (list of dicts)

[
 {'server':'serv1','os':'Linux','archive':'/my/folder1'}
 ,{'server':'serv2','os':'Linux','archive':'/my/folder1'}
 ,{'server':'serv3','os':'Linux','archive':'/my/folder2'}
 ,{'server':'serv4','os':'AIX','archive':'/my/folder1'}
 ,{'server':'serv5','os':'AIX','archive':'/my/folder1'}
]

TO (list of dicts with tuple as key and list of 'server#'s as value

[
 {('Linux','/my/folder1'):['serv1','serv2']}
 ,('Linux','/my/folder2'):['serv3']}
 .{('AIX','/my/folder1'):['serv4','serv5']}
]
MaxGrand
  • 113
  • 1
  • 9

2 Answers2

4

the need to be able to set default values to your dictionary and to have the same key several times may make a dict-comprehension a bit clumsy. i'd prefer something like this:

a defaultdict may help:

from collections import defaultdict

lst = [
 {'server':'serv1','os':'Linux','archive':'/my/folder1'},
 {'server':'serv2','os':'Linux','archive':'/my/folder1'},
 {'server':'serv3','os':'Linux','archive':'/my/folder2'},
 {'server':'serv4','os':'AIX','archive':'/my/folder1'},
 {'server':'serv5','os':'AIX','archive':'/my/folder1'}
]

dct = defaultdict(list)

for d in lst:
    key = d['os'], d['archive']
    dct[key].append(d['server'])

if you prefer to have a standard dictionary in the end (actually i do not really see a good reason for that) you could use dict.setdefault in order to create an empty list where the key does not yet exist:

dct = {}

for d in lst:
    key = d['os'], d['archive']
    dct.setdefault(key, []).append(d['server'])

the documentation on defaultdict (vs. setdefault):

This technique is simpler and faster than an equivalent technique using dict.setdefault()

hiro protagonist
  • 44,693
  • 14
  • 86
  • 111
1

It's difficult to achieve with list comprehension because of the accumulation effect. However, it's possible using itertools.groupby on the list sorted by your keys (use the same key function for both sorting and grouping).

Then extract the server info in a list comprehension and prefix by the group key. Pass the resulting (group key, server list) to dictionary comprehension and here you go.

import itertools

lst = [
 {'server':'serv1','os':'Linux','archive':'/my/folder1'}
 ,{'server':'serv2','os':'Linux','archive':'/my/folder1'}
 ,{'server':'serv3','os':'Linux','archive':'/my/folder2'}
 ,{'server':'serv4','os':'AIX','archive':'/my/folder1'}
 ,{'server':'serv5','os':'AIX','archive':'/my/folder1'}
]


sortfunc = lambda x : (x['os'],x['archive'])

result = {k:[x['server'] for x in v] for k,v in itertools.groupby(sorted(lst,key=sortfunc),key = sortfunc)}


print(result)

I get:

{('Linux', '/my/folder1'): ['serv1', 'serv2'], ('AIX', '/my/folder1'): ['serv4', 'serv5'], ('Linux', '/my/folder2'): ['serv3']}

Keep in mind that it's not because it can be written in one line that it's more efficient. The defaultdict approach doesn't require sorting for instance.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219