I have a python list which contains dictionaries and I want to make a new list which contain dictionaries with unique keys and associated list values like below:
Input:
[{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
Output:
[{1:[2,3,3]},{2:[2,1]}]
Thanks in advance.
How about:
input = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
r = {}
for d in input:
# (assumes just one key/value per dict)
((x, y),) = d.items()
r.setdefault(x, []).append(y)
print [ {k: v} for (k, v) in r.items() ]
Result:
[{1: [2, 3, 3]}, {2: [2, 1]}]
[update]
just curious : Can you explain whats going on in ((x, y),) = d.items() and r.setdefault(x, []).append(y) ? – damned
First the ((x, y),) = d.items():
at this point, d will be an element from input, like {1: 2}
d.items() will be something analogous to [(1, 2)]
in order to unpack 1 and 2 into x and y, we need the extra , (otherwise the interpreter will think the outer parenthesis are doing grouping instead of defining a single element tuple)
The r.setdefault(x, []).append(y) is analogous to:
if not r.has_key(x):
r[x] = []
r[x].append(y)
Trick is to use dict.setdefault to start off a list and append to it:
input = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
output = {}
for d in input:
for k,v in d.items():
output.setdefault(k, []).append(v)
# output contains {1: [2, 3, 3], 2: [2, 1]}
output=[{k:v} for k,v in output.items()]
# output contains [{1: [2, 3, 3]}, {2: [2, 1]}]
What setdefault does is return either the existing list keyed by 'k', or if that key does not exist in the dictionary, it creates a new entry for that key with the second argument and returns that. Either way it returns the list whether it was pre-existing or new, so that you can then append to it.
>>> lis=[{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
>>> new_lis=[{}]
>>> for x in lis:
for y in x:
if y in new_lis[0]:
new_lis[0][y].append(x[y])
new_lis[0][y].sort()
else :new_lis[0][y]=[x[y]]
>>> new_lis
[{1: [2, 3, 3], 2: [1, 2]}]
>>> data = [{1: 2}, {2: 2}, {1: 3}, {2: 1}, {1: 3}]
...
... from itertools import groupby
...
... keyFn = lambda x: x.keys()[0]
...
... [{k: [i.values()[0] for i in g]} for k, g in groupby(sorted(data, key=keyFn), keyFn)]
0: [{1: [2, 3, 3]}, {2: [2, 1]}]
output = []
for b in a:
added = 0
i = 0
for c in output:
if b.keys()[0] in c.keys():
output[i][b.keys()[0]].append(b[b.keys()[0]])
added = 1
i += 1
if not added:
output.append({b.keys()[0]: [b[b.keys()[0]]]})
Related
I'm looking for a Pythonic way to create a nested dictionary from a list and dictionary. Both the statements below produce the same result:
a = [3, 4]
b = {'a': 1, 'b': 2}
c = dict(zip(b, a))
c = {k: v for k, v in zip(b, a)}
Output is:
{3: 'a', 4: 'b'}
The desired result is:
{3: {'a': 1}, 4: {'b': 2}}
I could start using loops, but I don't believe that is necessary.
And off course, afterwards I will need to flatten those dictionaries again.
>>> {k: {va: vb} for k, (va, vb) in zip(a, b.items())}
{3: {'a': 1}, 4: {'b': 2}}
Like this:
a = [3, 4]
b = {'a': 1, 'b': 2}
c = {i: {k:b[k]} for i,k in zip(a,b)}
print(c)
Output:
{3: {'a': 1}, 4: {'b': 2}}
In my opinion, a more "Pythonic" way would be to use more descriptive variable names, and the dict() constructor:
keys = [3, 4]
orig_dict = {'a': 1, 'b': 2}
nested_dict = {key: dict([item]) for (key, item) in zip(keys, orig_dict.items())}
And another approach, using an intermediate iterable:
sub_dicts = [dict([item]) for item in orig_dict.items()]
nested_dict = dict(zip(keys, sub_dicts))
Finally, just using loops seems just fine in this case:
nested_dict = {}
for key, item in zip(keys, orig_dict.items()):
nested_dict[key] = dict([item])
I have a dict that looks like this:
{'word1/AB': 1,
'word2/AB': 2,
'word3/CD': 1,
'word4/EF': 1,
'word5/GH': 1}
And I need to end up with a 2D dict that looks like this:
{'word1':{'AB': 1},
'word2':{'AB': 2},
'word3':{'CD': 1},
'word4':{'EF': 1},
'word5':{'GH': 1}}
My regex for splitting the key is: "[a-zA-Z]*[^\/]"
But I'm absolutely stumped on how to go about making this. I believe I have to loop through the dict, split the key to two strings and construct the new dict using {str1:{str2: original dict value}} but my knowledge on dicts and slicing etc is shabby and I can't put my thought in code.
Could you help in determining what "tools" I need to do this?
Edit: I had totally forgot about .split(), however this wasn't the most important aspect of the question. Thanks all for reminding me though!
You can use str.split:
d = {'word1/AB': 1, 'word2/AB': 2, 'word3/CD': 1, 'word4/EF': 1, 'word5/GH': 1}
new_data = {tuple(a.split('/')):b for a, b in d.items()}
final_result = {a:{b:c} for [a, b], c in new_data.items()}
Output:
{'word1': {'AB': 1}, 'word2': {'AB': 2}, 'word3': {'CD': 1}, 'word4': {'EF': 1}, 'word5': {'GH': 1}}
You can just split your string on the '/' character, there's no need for regex here.
>>> d = {'word1/AB': 1,
...: 'word2/AB': 2,
...: 'word3/CD': 1,
...: 'word4/EF': 1,
...: 'word5/GH': 1}
...:
>>> result = {}
>>> for k, v in d.items():
...: k_outer, k_inner = k.split('/')
...: result[k_outer] = {k_inner:v}
...:
>>> result
>>>
{'word1': {'AB': 1},
'word2': {'AB': 2},
'word3': {'CD': 1},
'word4': {'EF': 1},
'word5': {'GH': 1}}
If you want to split on a regular expression you could use re.split(regex, stringToSplit):
import re
dict = {'word1/AB': 1, 'word2/AB': 2,'word3/CD': 1,'word4/EF': 1,'word5/GH': 1}
dict2d = {}
for key in dict:
values = re.split("\/", key);
dict2d[values[0]] = {values[1]:dict[key]}
print dict2d
Output
{'word5': {'GH': 1}, 'word4': {'EF': 1}, 'word1': {'AB': 1}, 'word3': {'CD': 1}, 'word2': {'AB': 2}}
However, for what you want to achieve i believe that key.split("\/") would be more than enough
You can split keys with str.split('/'). This one-liner will work:
db = {'word1/AB': 1, 'word2/AB': 2, 'word3/CD': 1, 'word4/EF': 1, 'word5/GH': 1}
{ k.split('/')[0]: {k.split('/')[1]: db[k]} for k in db.keys()}
I have 2 dicts like:
x = {2: {'a':1, 'b':1}}
y = {3: {'a':1}, 2: {'a':2, 'c':2}}
The merging result I want should be:
z = {3: {'a':1}, 2: {'a':3, 'b':1, 'c':2}}
I tried dict(x.items() + y.items()) and Counter, but only get
{2: {'a': 2, 'c': 2}, 3: {'a': 1}}
as a result.
How can I merge dict whose value is also a dict itself?
I have explained that the difference between How to merge two Python dictionaries in a single expression?
is that my dict's value is also a dict.
What about this:
For Python2.x:
from collections import Counter
y = {2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
x = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
t=x.copy()
print(dict({k: (dict(Counter(t.pop(k, None)) + Counter(v))) for k, v in y.items()},**t))
Result:
{1: {'a': 1, 'b': 1}, 2: {'a': 3, 'c': 2, 'b': 1}, 3: {'a': 1}}
For Python3.5+:
{**{k: (dict(Counter(t.pop(k, None)) + Counter(v))) for k, v in y.items()},**t}
Here you have a possible solution. Although it doesn't use any library, it is quite complex.
Also it will work with any two dictionaries, independently on which is the largest.
{key: {ikey: x.get(key, {}).get(ikey, 0) + y.get(key, {}).get(ikey, 0)
for ikey in x.get(key, {}).keys()+y.get(key, {}).keys()}
for key in x.keys()+y.keys()}
Output:
{2: {'a': 3, 'b': 1, 'c': 2}, 3: {'a': 1}}
Suppose for any same depth for a certain key, in two dicts the values are of same type (both dict or both number, i don't know how to define merging a number into a dict).
def merge(d1, d2):
for i in d2:
if i not in d1:
continue
if isinstance(d1[i], dict) and isinstance(d2[i], dict):
merge(d1[i], d2[i])
else:
d2[i] += d1[i]
d1.pop(i)
for j in d1:
d2[j] = d1[j]
It's straightforward but maybe not elegant.
Use collections.Counter to "merge" the items you want to merge (only the common keys), and leave the others intact, using a double ternary expression in a dict comprehension, iterating on the union of the keys:
import collections
x = {2: {'a':1, 'b':1}}
y = {3: {'a':1}, 2: {'a':2, 'c':2}}
# pre-compute common keys, one-liners are not the ultimate goal in life!
common_keys = set(x) & set(y)
# now generate the merged dict
result = {k:collections.Counter(x[k])+collections.Counter(y[k]) if k in common_keys else x[k] if k in x else y[k] for k in set(x)|set(y)}
print(result)
result:
{2: Counter({'a': 3, 'c': 2, 'b': 1}), 3: {'a': 1}}
Note: we could have avoided computing the common keys beforehand by replacing:
if k in common_keys
by
if k in x and k in y
This works:
x = {2: {'a':1, 'b':1}}
y = {3: {'a':1}, 2: {'a':2, 'c':2}}
def key_merge (dict1, dict2):
## function which checks for any matching keys, and merges them
if len(dict1.keys()) == 0 or len(dict2.keys()) == 0:
return {}
else:
for key in dict1.keys():
if key in dict2.keys():
return {key:{ k: dict1[key].get(k, 0) + dict2[key].get(k, 0) for k in set(dict1[key])|set(dict2[key]) }}
z = {**x, **y, **key_merge(x, y)}
Again, up to you if it's elegant!
I don't know what your definition of 'elegantly' is but assuming you mean readable then perhaps this way will suit you.
from collections import Counter
def counter_dict(in_dict):
"""
Create a dict of Counters from a dict of dicts.
"""
return dict((k, Counter(v)) for (k, v) in in_dict.items())
def merge_counter_dicts(a, b):
"""
Create a dict of Counters from two dicts of Counters.
Where keys exist in both counts are summed.
"""
out_dict = a.copy()
for k in b.keys():
out_dict[k] = out_dict.setdefault(k, Counter()) + b[k]
return out_dict
x = {2: {'a': 1, 'b': 1}}
y = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
xc = counter_dict(x)
xy = counter_dict(y)
print merge_counter_dicts(xc, xy)
# What about something like this:
# merge_nested_dicts.py
# tested under Python3.6
# assuming dict_02 overwrites dict_01
# one liner functional style
def deep_merge(dict_01, dict_02):
return {k: {**dict_01.get(k), **dict_02.get(k)} if k in dict_01 and
isinstance(dict_01.get(k), dict) and
isinstance(dict_02.get(k), dict) else v
for k, v in {**dict_01, **dict_02}.items()}
if __name__ == '__main__':
y = {2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
x = {3: {'a': 1}, 2: {'a': 2, 'c': 2}}
print(x)
print(y)
print(deep_merge(x, y))
'''
{3: {'a': 1}, 2: {'a': 2, 'c': 2}}
{2: {'a': 1, 'b': 1}, 1: {'a': 1, 'b': 1}}
{3: {'a': 1}, 2: {'a': 1, 'c': 2, 'b': 1}, 1: {'a': 1, 'b': 1}}
'''
I want to convert a list of lists to a list of dictionaries. I have a way to do it but I suspect there's a better way:
t = [[1,2,3], [4,5,6]]
keys = ['a', 'b', 'c']
[{keys[0]:l[0], keys[1]:l[1], keys[2]:l[2]} for l in t]
with output
[{'a': 1, 'c': 3, 'b': 2}, {'a': 4, 'c': 6, 'b': 5}]
This could be done with a loop, but I bet there's a function to do it even easier. From this answer I'm guessing there's a way to do it with the map command, but I'm not quite sure how.
You can use list comprehension with the dict() constructor and zip:
[dict(zip(keys, l)) for l in t ]
Demo
>>> d = [dict(zip(keys, l)) for l in t ]
>>>
>>> d
[{'a': 1, 'c': 3, 'b': 2}, {'a': 4, 'c': 6, 'b': 5}]
>>>
It can also be solved with a dictionary comprehension, this way:
>>> [{k:v for k,v in zip(keys, l)} for l in t]
[{'c': 3, 'b': 2, 'a': 1}, {'c': 6, 'b': 5, 'a': 4}]
I have a list of dicts:
d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
I want to remove the curly braces and convert d to a single dict which looks like:
d ={'a': 4, 'b': 20, 'c': 5, 'd': 3}
If you don't mind duplicate keys replacing earlier keys you can use:
from functools import reduce # Python 3 compatibility
d = reduce(lambda a, b: dict(a, **b), d)
This merges the first two dictionaries then merges each following dictionary into the result built so far.
Demo:
>>> d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
>>> reduce(lambda a, b: dict(a, **b), d)
{'a': 4, 'c': 5, 'b': 20, 'd': 3}
Or if you need this to work for arbitrary (non string) keys (and you are using Python 3.5 or greater):
>>> d =[{4: 4}, {20: 20}, {5: 5}, {3: 3}]
>>> reduce(lambda a, b: dict(a, **b), d) # This wont work
TypeError: keywords must be strings
>>> reduce(lambda a, b: {**a, **b}, d) # Use this instead
{4: 4, 20: 20, 5: 5, 3: 3}
The first solution hacks the behaviour of keyword arguments to the dict function. The second solution is using the more general ** operator introduced in Python 3.5.
You just need to iterate over d and append (update()) the element to a new dict e.g. newD.
d =[{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
newD = {}
for entry in d:
newD.update(entry)
>>> newD
{'c': 5, 'b': 20, 'a': 4, 'd': 3}
Note: If there are duplicate values in d the last one will be appear in newD.
Overwriting the values of existing keys, a brutal and inexperienced solution is
nd = {}
for el in d:
for k,v in el.items():
nd[k] = v
or, written as a dictionary comprehension:
d = {k:v for el in d for k,v in el.items()}
a = [{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
b = {}
[b.update(c) for c in a]
b = {'a': 4, 'b': 20, 'c': 5, 'd': 3}
if order is important:
from collections import OrderedDict
a = [{'a': 4}, {'b': 20}, {'c': 5}, {'d': 3}]
newD = OrderedDict()
[newD.update(c) for c in a]
out = dict(newD)