Compact list of dictionaries into into single dictionary? - python

I have a list of dictionaries like the following:
l = [{0: [1L, 743.1912508784121]}, {0: [2L, 148.34440427559701]}, {0: [5L, 1275.9155165676464]}, {0: [6L, 128.46132477853394]}, {0: [8L, 1120.5549823618721]}, {0: [9L, 1000.4359061629533]}, {0: [10L, 1000.4359061629533]}, {0: [11L, 1148.2027994669606]}, {0: [12L, 222.1206974476257]}, {0: [15L, 1024.0437005257695]}, {1: [8L, 606.0185176629063]}, {1: [13L, 115.54464589045607]}, {1: [14L, 1057.134622491455]}, {1: [16L, 1000.346200460439]}, {1: [17L, 285.73897308106336]}, {2: [3L, 941.8651982485691]}, {2: [4L, 1001.6313224538114]}, {2: [7L, 1017.0693313362076]}, {2: [11L, 427.7241587977401]}]
in this specific case the list has 19 dictionaries with 3 different keys (0,1,2).
What I'm trying to do is to transform it into a single dictionary where the values of each key is made by another dictionary.
So for example, extracting 4 elements of the list, I'd like to compact this:
l = [{0: [1L, 743.1912508784121]}, {0: [2L, 148.34440427559701]}, {1: [13L, 115.54464589045607]}, {1: [14L, 1057.134622491455]}]
into:
d = {0:{1L: 743.1912508784121, 2L: 148.34440427559701}, 1:{13L: 115.54464589045607, 14L: 1057.134622491455}}
I hope I made myself clear

This'll work, hopefully the code should be fairly self-explanatory.
Note that you'll need to use dictionary.iteritems() in Python 2.x, as dictionary.items() is Python 3.x only.
d ={}
for dictionary in l:
for key, (k, v) in dictionary.items():
if key not in d:
d[key] = {}
d[key][k] = v

You may get this result using collections.defaultdict as:
from collections import defautdict
my_dict = defaultdict(dict)
for d in l:
for k, (v1, v2) in d.items():
my_dict[k][v1] = v2
where my_dict will hold the final value as:
{0: {1L: 743.1912508784121, 2L: 148.34440427559701, 5L: 1275.9155165676464, 6L: 128.46132477853394, 8L: 1120.5549823618721, 9L: 1000.4359061629533, 10L: 1000.4359061629533, 11L: 1148.2027994669606, 12L: 222.1206974476257, 15L: 1024.0437005257695}, 1: {8L: 606.0185176629063, 16L: 1000.346200460439, 13L: 115.54464589045607, 14L: 1057.134622491455, 17L: 285.73897308106336}, 2: {11L: 427.7241587977401, 3L: 941.8651982485691, 4L: 1001.6313224538114, 7L: 1017.0693313362076}}
Note: Since dict can have unique keys, it will have the value of nested dict based on the last value in the list.

Related

How to convert dict of dict to dict of specified format?

I have a dictionary of dictionaries as shown below:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
and I would want it be in required format:
result = {1:["hello", "!"], 2: ["How are you", "?"]}
However, I get this in the following format using the code below:
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).append(value)
The result is not of required structure and it causes a list of lists.
{1: [['hello'], ['!']], 2: [['How are you'], ['?']]}
Any help here would be highly appreciated. Thanks.
use extend instead of append:
new_d.setdefault(key, []).extend(value)
The extend() method adds all the elements of an iterable (list, tuple, string etc.) to the end of the list.
If you want to solve this problem with using append() function try this code:
new_d = {}
for sub in d.values():
for key, value in sub.items():
# Control key exist...
if(key in new_d.keys()):
new_d[key].append(value[0])
else:
new_d[key] = value
You can either use .extend(value) instead of .append(value)
or you can add a basic for loop to flatten the list of all dictionary values as shown below.
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).extend(value)
for i in range (0,len(d)):
new_d[i+1] = [item for sublist in new_d.get(i+1) for item in sublist]
print(new_d)
The accepted answer by #Gabip correctly identifies that your only mistake was using append instead of extend.
That mistake being corrected, I'd also like to suggest a slightly different approach using dict comprehensions:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in d[0]}
# {1: ['hello', '!'], 2: ['How are you', '?']}
Or a more robust version that takes keys from both d[0] and d[1], in case some keys are in d[1] but not in d[0]:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"], 3: ['>>>']}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in set(d[0].keys()) | set(d[1].keys())}
# {1: ['hello', '!'], 2: ['How are you', '?'], 3: ['>>>']}
Finally, this wasn't explicitly part of your question, but I suggest using str.join to join the strings:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: ''.join(d[0].get(key, []) + d[1].get(key, [])) for key in d[0]}
# {1: 'hello!', 2: 'How are you?'}

How can i combine a list of dicts to a list of dicts combining like keys

I want to take this input:
mycounter = [{6: ['Credit card']}, {2: ['Debit card']}, {2: ['Check']}]
#[{6: ['Credit card']}, {2: ['Debit card']}, {2: ['Check']}]
And achieve this Desired Output:
[{6: ['Credit card']}, {2: ['Debit card', 'Check']}]
My attempt was the following, but it's not matching desired output. Any help here is appreciated. Thx.
temp = list(zip([*map(lambda d: next(iter(d.keys())), mycounter)], [*map(lambda d: next(iter(d.values())), mycounter)]))
c = collections.defaultdict(list)
for a,b in temp:
c[a].extend(b)
final = [dict(c)]
# Close, but not quite the desired output since it's should be two dict objects, not one
# [{6: ['Credit card'], 2: ['Debit card', 'Check']}]
My searches on stackoverflow found solutions that combine with giving None values, but nothing like what i'm asking for. My question has a different input than another similar question earlier.
For the sake of giving options, here is an alternate version that uses defaultdict, which I find easier to use especially as complexity increases:
from collections import defaultdict
result = defaultdict(list)
for d in mycounter:
for k, v in d.items():
result[k] += v
In : result
Out: defaultdict(list, {6: ['Credit card'], 2: ['Debit card', 'Check']})
Defaultdict behaves like dict for the most part but if necessary it can be converted to one with:
In : dict(result)
Out: {6: ['Credit card'], 2: ['Debit card', 'Check']}
One option would be to make a single dictionary then break it into single key-value pairs after:
mycounter = [{6: ['Credit card']}, {2: ['Debit card']}, {2: ['Check']}]
res = {}
for d in mycounter:
for k, v in d.items():
res.setdefault(k, []).extend(v)
[{k:v} for k, v in res.items()]
# [{6: ['Credit card']}, {2: ['Debit card', 'Check']}]
This is very similar to another question; the only difference is that you want each key/value pair in its own dictionary. Here's an adapted solution using comprehensions and itertools:
from itertools import chain
def merge_dicts(*dicts):
return [
{ k: list(chain.from_iterable( d[k] for d in dicts if k in d )) }
for k in set(chain.from_iterable(dicts))
]

How to group Python dictionary values which are dictionaries themselves

I'd like to group by the values of the following dictionary:
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, Q4: {1: "animal", 2: "vehicle"}}
The result should look like this:
result = {("Q1", "Q2"): {0: "no", 1: "yes"},
("Q3", "Q4"): {1: "animal", 2: "vehicle"}}
I've tried the solutions listed here:
Grouping Python dictionary keys as a list and create a new dictionary with this list as a value
Using collections.defaultdict does not work because the result would imply that the dictionaries which I use as a key for grouping end up as keys of the result dictionary like that:
result = {{0: "no", 1: "yes"}: ["Q1", "Q2"] ,
{1: "animal", 2: "vehicle"}: ["Q3", "Q4"]}
Of course this does not work because keys of dictionaries have to be immutible. So I would require something like a frozendict which is not available in the standard library of python.
Using itertools.groupby also does not work because it requires the data to be sorted. But operator.itemgetter cannot sort dictionaries. It says:
TypeError: '<' not supported between instances of 'dict' and 'dict'
Therefore, I'd like to know a Pythonic way of solving this problem! Thank you for your help :)
Instead of using frozendict, you can use frozenset's of the dictionaries' items:
intermediate_dict = defaultdict(list)
for k, v in my_dict.items():
intermediate_dict[frozenset(v.items())].append(k)
result = {tuple(v): dict(k) for k, v in intermediate_dict.items()}
Output:
{('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
The above does not assume or require sorted input, making it O(n) for all cases, while sorting is O(n log n).
Assuming a sorted dictionary by value, you can use itertools.groupby:
{tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)}
Code:
from itertools import groupby
my_dict = {"Q1": {0: "no", 1: "yes"}, "Q2": {0: "no", 1: "yes"},
"Q3": {1: "animal", 2: "vehicle"}, "Q4": {1: "animal", 2: "vehicle"}}
print({tuple(g): k for k, g in groupby(my_dict, key=my_dict.get)})
# {('Q1', 'Q2'): {0: 'no', 1: 'yes'}, ('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}}
So I would require something like a frozendict which is not available in the standard library of python.
Could you elaborate on this? While frozendict is not in the language standard, there's an extension available that you could install: https://pypi.org/project/frozendict/
Alternatively, you can turn the dictionaries into a tuple of (key-sorted) (key, value) items to get an immutable, canonical and reversible representation that can be used as a dictionary key.
(Note that if the dictionaries can have further mutable values inside them, you might need to do this recursively.)
Edit: Or use a frozenset() for the items, as the other answer points out. Note that this also requires recursively ensuring the values of the inner dictionary are immutable.
Here is another way using both frozenset and groupby
from operator import itemgetter
from itertools import groupby
first = itemgetter(0)
second = itemgetter(1)
my_hashes = sorted([(k, hash(frozenset(v))) for k, v in my_dict.items()], key=second)
d = dict()
for k, v in groupby(my_hashes, key=second):
items = list(v)
d[tuple(map(first, items))] = my_dict.get(first(first(items)))
print(d)
{('Q3', 'Q4'): {1: 'animal', 2: 'vehicle'}, ('Q1', 'Q2'): {0: 'no', 1: 'yes'}}

Python dictionary print all values for all keys

I have two python lists:
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
The "keys" are cluster ID list for the corresponding words in "values" list. I wish to print key-value pairs using
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
dictionary = dict(zip(keys, values))
for key, value in dictionary.items() :
print (key, value)
But it only prints
1 apple
2 paper
3 tennis
What I actually want is to get all values for all keys like this
1 [apple]
2 [book,pen,paper]
3 [soccer,tennis]
I know that my current code should logically print the first output as keys are unique. But how can I change it so that it will print all values for all keys? Thank you in advance!
from collections import defaultdict
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = defaultdict(list)
for k, v in zip(keys, values):
d[k].append(v)
Looks like what you want is a mapping from one key to multiple values, one way to accomplish it would be:
from collections import defaultdict
d = defaultdict(list)
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
for tuple in zip(keys, values):
d[tuple[0]].append(tuple[1])
print(d) # defaultdict(<class 'list'>, {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']})
You can use itertools:
import itertools
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
final_data = {a:[i[0] for i in b] for a, b in [(a, list(b)) for a, b in itertools.groupby(sorted(zip(values, keys), key=lambda x:x[-1]), key=lambda x:x[-1])]}
Output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
pure python also works
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = dict(zip(keys, [[] for _ in keys])) # dict w keys, empty lists as values
for k, v in zip(keys, values):
d[k].append(v)
d
Out[128]: {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Two method :
If you want you can use default dict as many already have been suggested :
Data is :
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
Method: 1
import collections
d=collections.defaultdict(list)
for i in zip(keys,values):
d[i[0]].append(i[1])
print(d)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Or if you want to develop your own logic without importing any external module then you can try:
result={}
for i in zip(keys,values):
if i[0] not in result:
result[i[0]]=[i[1]]
else:
result[i[0]].append(i[1])
print(result)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}

How to add new values to key in a dict?

If I had a sample dict:
mydict = {1:{2:'A'}}
how can I add new data to key 1 so that it can be like:
mydict = {1:{2:'A'},{3:'B'}}
When I do mydict.update({3:'B'})
I get:
{1: {2: 'A'}, 3: 'B'} not {1: {2: 'A'}, {3: 'B'}}
If I try .update using the same key it just replaces the data:
mydict.update({1:{3:'B'}}) becomes
{1: {3: 'B'}}
There already have a good library for you:
from collections import defaultdict
d = defaultdict(dict)
d['person']['name']='frank'
d['person']['age']='age'
and then you can easily get the data from your 2 key! like:
print d['person']['name']
When you do mydict.update({3:'B'}) you are updating the wrong dictionary.
It looks like you want to update the dictionary in mydict[1]
This is assuming you actually want a result of {1: {2: 'A', 3: 'B'}} as your example {1: {2: 'A'}, {3: 'B'}} is not valid python.
So try mydict[1].update({3:'B'})
Simply add
mydict[1].update({3:'B'})
mydict ={2:'a',3:'b'}
d={1:mydict}
print (d)
{1: {2: 'a', 3: 'b'}}
to update more key:values you can add to mydict

Categories