make 2D dict from dict python by splitting keys with regex - python

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()}

Related

Pythonic way to create a nested dictionary

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])

Unpack list of dictionaries in Python

Question
According to this answer, in Python 3.5 or greater, it is possible to merge two dictionaries x and y by unpacking them:
z = {**x, **y}
Is it possible to unpack a variadic list of dictionaries? Something like
def merge(*dicts):
return {***dicts} # this fails, of course. What should I use here?
For instance, I would expect that
list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
{***list_of_dicts} == {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Note that this question is not about how to merge lists of dictionaries since the link above provides an answer to this. The question here is: is it possible, and how, to unpack lists of dictionaries?
Edit
As stated in the comments, this question is very similar to this one. However, unpacking a list of dictionaries is different from simply merging them. Supposing that there was an operator *** designed to unpack lists of dictionaries, and given
def print_values(a, b, c, d):
print('a =', a)
print('b =', b)
print('c =', c)
print('d =', d)
list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
it would be possible to write
print_values(***list_of_dicts)
instead of
print_values(**merge(list_of_dicts))
Another solution is using collections.ChainMap
from collections import ChainMap
dict(ChainMap(*list_of_dicts[::-1]))
Out[88]: {'a': 1, 'b': 2, 'c': 3, 'd': 4}
You could just iterate over the list and use update:
lst = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
dct = {}
for item in lst:
dct.update(item)
print(dct)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}
There's no syntax for that, but you can use itertools.chain to concatenate the key/value tuples from each dict into a single stream that dict can consume.
from itertools import chain
def merge(*dicts):
return dict(chain.from_iterable(d.items() for d in dicts))
You can also unpack a list created by a list comprehension as well:
def merge(*dicts):
return dict(*[d.items() for d in dicts])
To merge multiple dictionaries you can use the function reduce:
from functools import reduce
lst = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
reduce(lambda x, y: dict(**x, **y), lst)
# {'a': 1, 'b': 2, 'c': 3, 'd': 4}
You could use list comprehension and put this iterable object as an argument to dict
def merge(*dicts):
lst = [*[d.items() for d in dicts]]
return dict(lst)
You can just use a list comprehension to iterate over all the dicts in the list and then iterate over each if those dicts' items and finally convert them to dict
>>> lst = [{'a':1}, {'b':2}, {'c':1}, {'d':2}]
>>> dict(kv for d in lst for kv in d.items())
{'a': 1, 'b': 2, 'c': 1, 'd': 2}
You can use reduce to merge two dicts at a time using dict.update
>>> from functools import reduce
>>> lst = [{'a':1}, {'b':2}, {'c':1}, {'d':2}]
>>> reduce(lambda d1, d2: d1.update(d2) or d1, lst, {})
{'a': 1, 'b': 2, 'c': 1, 'd': 2}
When you *dicts its put in as a tuple, you can pull the list out with d[0], then use this comprehension for nonuniform keys
list_of_dicts = [{'a': 1, 'b': 2}, {'c': 3}, {'d': 4}]
def merge(*dicts):
return dict( j for i in dicts[0] for j in i.items())
print(merge(list_of_dicts))
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
[Program finished]

Sum values in subdicts by key

Having a dict like:
x = {
'1': {'a': 1, 'b': 3},
'2': {'a': 2, 'b': 4}
}
I'd like to have a new key total with the sum of each key in the subdictionaries, like:
x['total'] = {'a': 3, 'b': 7}
I've tried adapting the answer from this question but found no success.
Could someone shed a light?
Assuming all the values of x are dictionaries, you can iterate over their items to compose your new dictionary.
from collections import defaultdict
x = {
'1': {'a': 1, 'b': 3},
'2': {'a': 2, 'b': 4}
}
total = defaultdict(int)
for d in x.values():
for k, v in d.items():
total[k] += v
print(total)
# defaultdict(<class 'int'>, {'a': 3, 'b': 7})
A variation of Patrick answer, using collections.Counter and just update since sub-dicts are already in the proper format:
from collections import Counter
x = {
'1': {'a': 1, 'b': 3},
'2': {'a': 2, 'b': 4}
}
total = Counter()
for d in x.values():
total.update(d)
print(total)
result:
Counter({'b': 7, 'a': 3})
(update works differently for Counter, it doesn't overwrite the keys but adds to the current value, that's one of the subtle differences with defaultdict(int))
You can use a dictionary comprehension:
x = {'1': {'a': 1, 'b': 3}, '2': {'a': 2, 'b': 4}}
full_sub_keys = {i for b in map(dict.keys, x.values()) for i in b}
x['total'] = {i:sum(b.get(i, 0) for b in x.values()) for i in full_sub_keys}
Output:
{'1': {'a': 1, 'b': 3}, '2': {'a': 2, 'b': 4}, 'total': {'b': 7, 'a': 3}}
from collections import defaultdict
dictionary = defaultdict(int)
x = {
'1': {'a': 1, 'b': 3},
'2': {'a': 2, 'b': 4}
}
for key, numbers in x.items():
for key, num in numbers.items():
dictionary[key] += num
x['total'] = {key: value for key, value in dictionary.items()}
print(x)
We can create a default dict to iterate through each of they key, value pairs in the nested dictionary and sum up the total for each key. That should enable a to evaluate to 3 and b to evaluate to 7. After we increment the values we can do a simple dictionary comprehension to create another nested dictionary for the totals, and make a/b the keys and their sums the values. Here is your output:
{'1': {'a': 1, 'b': 3}, '2': {'a': 2, 'b': 4}, 'total': {'a': 3, 'b': 7}}

Convert list of lists to list of dictionaries

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}]

Python dictionary with same keys

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]]]})

Categories

Resources