Transform list of dictionaries into dict of dicts - python

What is the easiest way to transform this list of dictionaries:
[
{'code': 1, name:'A', value:'1'},
{'code': 1, name:'B', value:'2'},
{'code': 2, name:'C', value:'3'},
{'code': 2, name:'D', value:'4'},
{'code': 3, name:'E', value:'5'},
{'code': 3, name:'F', value:'6'},
......
]
into this:
{
1: [{name:'A', value:'1'}, {name:'B', value:'2'}],
2: [{name:'C', value:'3'}, {name:'D', value:'4'}],
3: [{name:'E', value:'5'}, {name:'F', value:'6'}],
...
}
I would like a nice solution like list comprehension, not the hard way.
Thanks

A list comprehension would require an (inefficient) sorting operation, then grouping.
It's much easier to use a dictionary and a loop:
res = {}
for entry in inputlist:
code = entry.pop('code')
res.setdefault(code, []).append(entry)
This alters the original dictionaries; create a copy first if this is an issue:
entry = dict(entry)
code = entry.pop('code')
If your input list is already sorted, then you could use itertools.groupby() to produce the dictionary in a one-liner, but the readability of this solution is debatable:
from itertools import groupby
from operator import itemgetter
res = {key: [{k: v for k, v in d.items() if k != 'code'} for d in group]
for key, group in groupby(inputlist, itemgetter('code'))}

Related

Is there a way to use temporary list variable inside nested dict comprehension?

this is what my dict looks like:
ranked_imgs = {'d57b26aa-c8f8-4e08-9e8f-8aecd6e952a5': {'small': [],
'medium': [['https://www.pablo-ruiz-picasso.net/images/works/261.jpg', 1]],
'large': []},
'b10ecfdb-f590-4930-9975-aa12dc267f2f': {'small': [],
'medium': [],
'large': [['https://www.pablo-ruiz-picasso.net/images/works/3936.jpg', 1]]},......}
This is what I'm trying to do using dict comprehension but it fails because the temporary list is over ridden due to for loop:
dct = {k:[x for x in vv] for k,v in ranked_imgs.items() for vk,vv in v.items()}
or something like this maybe :
dct = {k:sum(vv,[]) for k,v in ranked_imgs.items() for vk,vv in v.items()}
This is what the code will look like with comprehension:
# ranked_imgs_={}
# for k,v in ranked_imgs.items():
# lst = []
# for vk,vv in v.items():
# for url in vv:
# lst.append(url)
# ranked_imgs_[k] = lst
I'm curious to know if there is a pythonic way to do this using dict comprehension!
Your expected output is a dict of lists, so you can use a list comprehension as the output value of a dict comprehension. Also note that you can iterate over v.values() instead of v.items() since you aren't using the keys (thanks to #MechanicPig for pointing it out):
dct = {k: [url for vv in v.values() for url in vv] for k, v in ranked_imgs.items()}
Use chain.from_iterable may be a little better than the answer of #blhsing, it iterates by concatenating v.values():
from itertools import chain
result = {k: list(chain.from_iterable(v.values())) for k, v in ranked_imgs.items()}

combine two python lists of dict with index

I want to merge two lists into a nested construct.
List_A: [{'id':1,'val':'abc'},{'id':2,'val':'bcd'},{'id':3,'val':'efg'}]
List_B: [{'ref':1,'name':'xyz'},{'ref':2,'name':'opq'},{'ref:2,'name':'jkl'},{'ref':3,'name':'nmo'}]
Result should be:
List_C: [{'id':1,'list_b':[{'name':'xyz'}]},{'id':2,'list_b':[{'name':'opq'},{'name':'jkl'}]},{'id':3,'list_b':[{'name':'nmo'}]}]
I tried pandas join and merge but got no result. Sure, I could iterate through the list and do it key by key, but there might be a much better solution.
Based on your desired output, you don't need List_A at all.
from collections import defaultdict
b = [{'ref':1,'name':'xyz'}, {'ref':2,'name':'opq'}, {'ref':2,'name':'jkl'},{'ref':3,'name':'nmo'}]
dct = {}
[dct.setdefault(tuple(i), list()).append(j) for *i, j in [tuple(x.values()) for x in b]]
[{'id':k[0], 'list_b':list({'name': n} for n in v)} for k, v in dct.items()]
Output:
[{'id': 1, 'list_b': [{'name': 'xyz'}]},
{'id': 2, 'list_b': [{'name': 'opq'}, {'name': 'jkl'}]},
{'id': 3, 'list_b': [{'name': 'nmo'}]}]
Overall this seems like an over-complicated way to store the data, but if you need that specific format (ex for an input to another program) then w/e.
from copy import deepcopy
List_C = deepcopy(List_A)
for dct1 in List_C:
dct1.pop('val')
dct1['list_b'] = list()
for dct2 in List_B:
if dct1['id'] == dct2['ref']:
dct3 = dct2.copy()
dct3.pop('ref')
dct1['list_b'].append(dct3)

How to print a dictionary as key and count (if values is list) without for loop?

Suppose I have a dictionary as follows
dic = {0: [1,2,3,4,5], 1:[7,4,6]}
While printing the dictionary as key and count, first updating the dictionary
for k, v in dic.items():
dic[k] = len(v)
print(dic)
>>> {0:5, 1:3}
Is there a better way to do the above without for loop?
What do you mean saying without iteration? If you don't want to use a for loop, you can use a map function:
d = dict(map(lambda kv: (kv[0], len(kv[1])), d.items()))
If by no iteration you simply mean to do it without a for loop, you can map the dict values to the len function, zip them with the dict keys and pass the zipped key-value pairs to the dict constructor:
>>> d = {0: [1,2,3,4,5], 1:[7,4,6]}
>>> dict(zip(d, map(len, d.values())))
{0: 5, 1: 3}
>>>
First of all do not name a list 'list' or a dictionary 'dict', as this is a reserved word in python for the class that holds that data type.
You can do it neatly using dictionary comprehension as follows:
d = {0: [1,2,3,4,5], 1:[7,4,6]}
d = {i:len(j) for i,j in d.items()}
print(d)
Or use:
print({k:len(dic[k]) for k in dic})
Output:
{0: 5, 1: 3}

Counting distinct dictionary values

I have this dictionary (key,list)
index={'chair':['one','two','two','two'],'table':['two','three','three']}
and i want this
#1. number of times each value occurs in each key. ordered descending
indexCalc={'chair':{'two':3,'one':1}, 'table':{'three':2,'two':1}}
#2. value for maximum amount for each key
indexMax={'chair':3,'table':2}
#3. we divide each value in #1 by value in #2
indexCalcMax={'chair':{'two':3/3,'one':1/3}, 'table':{'three':2/2,'two':1/2}}
I think I should use lambda expressions, but can't come up with any idea how i can do that. Any help?
First, define your values as lists correctly:
index = {'chair': ['one','two','two','two'], 'table': ['two','three','three']}
Then use collections.Counter with dictionary comprehensions:
from collections import Counter
number of times each value occurs in each key.
res1 = {k: Counter(v) for k, v in index.items()}
value for maximum amount for each key
res2 = {k: v.most_common()[0][1] for k, v in res1.items()}
we divide each value in #1 by value in #2
res3 = {k: {m: n / res2[k] for m, n in v.items()} for k, v in res1.items()}
index={'chair':{'one','two','two','two'},'table':{'two','three','three'}}
Problem: {} is creating a set. So you should consider to convert it into list.
Now coming to your solution:
from collections import Counter
index={'chair': ['one','two','two','two'],'table':['two','three','three']}
updated_index = {'chair': dict(Counter(index['chair'])), 'table': dict(Counter(index['table']))}
updated_index_2 = {'chair': Counter(index['chair']).most_common()[0][1], 'table': Counter(index['table']).most_common()[0][1]}
print(updated_index)
print(updated_index_2)
You can use python collections library, Counter to find the count without writing any lambda function.
{'chair': {'one': 1, 'two': 3}, 'table': {'two': 1, 'three': 2}}
{'chair': 3, 'table': 2}
Firstly, you have a mistake in how you created the index dict. You should have lists as the elements for each dictionary, you currently have sets. Sets are automatically deduplicated, so you will not be able to get a proper count from there.
You should correct index to be:
index={'chair':['one','two','two','two'],'table':['two','three','three']}
You can use the Counter module in Python 3, which is a subclass of the dict module, to generate what you want for each entry in indexCalc. A counter will create a dictionary with a key, and the number of times that key exists in a collection.
indexCalc = {k, Counter(v) for k, v in index}
indexCalc looks like this:
{'chair': Counter({'two': 3, 'one': 1}), 'table': Counter({'three': 2, 'two': 1})}
We can easily find the index that corresponds to the maximum value in each sub-dictionary:
indexMax = {k: max(indexCalc[k].values()) for k in indexCalc}
indexMax looks like this:
{'chair': 3, 'table': 2}
You can create indexCalcMax with the following comprehension, which is a little ugly:
indexCalcMax = {k: {val: indexCalc[k][val] / indexMax[k] for val in indexCalc[k]} for k in indexCalc}
which is a dict-comprehension translation of this loop:
for k in indexCalc:
tmp = {}
for val in indexCalc[k]:
tmp[val] = indexCalc[k][val] / float(indexMax[k])
indexCalcMax[k] = tmp
I know this is suboptimal, but I had to do it as a thought exercise:
indexCalc = {
k: {key: len([el for el in index[k] if el == key]) for key in set(index[k])}
for k in index
}
Not exactly lambda, as suggested, but comprehensions... Don't use this code in production :) This answer is only partial, you can use the analogy and come up with the other two structures that you require.

Dictionary comprehension for swapping keys/values in a dict with multiple equal values

def invert_dict(d):
inv = dict()
for key in d:
val = d[key]
if val not in inv:
inv[val] = [key]
else:
inv[val].append(key)
return inv
This is an example from Think Python book, a function for inverting(swapping) keys and values in a dictionary. New values (former keys) are stored as lists, so if there was multiple dictionary values (bound to a different keys) that were equal before inverting, then this function simply appends them to the list of former keys.
Example:
somedict = {'one': 1, 'two': 2, 'doubletwo': 2, 'three': 3}
invert_dict(somedict) ---> {1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
My question is, can the same be done with dictionary comprehensions? This function creates an empty dict inv = dict(), which is then checked later in the function with if/else for the presence of values. Dict comprehension, in this case, should check itself. Is that possible, and how the syntax should look like?
General dict comprehension syntax for swapping values is:
{value:key for key, value in somedict.items()}
but if I want to add an 'if' clausule, what it should look like? if value not in (what)?
Thanks.
I don't think it's possible with simple dict comprehension without using other functions.
Following code uses itertools.groupby to group keys that have same values.
>>> import itertools
>>> {k: [x[1] for x in grp]
for k, grp in itertools.groupby(
sorted((v,k) for k, v in somedict.iteritems()),
key=lambda x: x[0])
}
{1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
You can use a set comprehension side effect:
somedict = {'one': 1, 'two': 2, 'doubletwo': 2, 'three': 3}
invert_dict={}
{invert_dict.setdefault(v, []).append(k) for k, v in somedict.items()}
print invert_dict
# {1: ['one'], 2: ['doubletwo', 'two'], 3: ['three']}
Here is a good answer:
fts = {1:1,2:1,3:2,4:1}
new_dict = {dest: [k for k, v in fts.items() if v == dest] for dest in set(fts.values())}
Reference: Head First Python ,2nd Edition, Page(502)

Categories