I am given a list of dictionaries like this:
[
{'A': ['B', 'C', 'D']},
{'B': ['E', 'F']},
{'C': ['E']},
{'F': ['G', 'H']}
]
A key in the dictionary is a parent of a corresponding dictionary value, i.e., {parent: [child1, child2, child3]}
How can I construct a tree-like dictionary in the following format like the following:
{'A':{'B':{'E': None,
'F': {'G': None,
'H': None}
},
'C': {'E': None}}
If a node doesn't have a child, we will fill its value with None. I don't know how to write a recursive process to transform the list into a dictionary, any idea?
Thanks!
You can do this with a recursive function:
def find_set(d, k, v):
for key, value in d.items():
if isinstance(value, dict):
find_set(d[key], k, v)
return
if key == k:
d[key] = {}
for i in v:
d[key][i] = None
return d
d[k] = {}
for i in v:
d[k][i] = None
return d
Code:
l = [{'A': ['B', 'C', 'D']}, {'B': ['E', 'F']}, {'C': ['E']}, {'F': ['G', 'H']}]
d = {}
for node in l:
for key, value in node.items():
find_set(d, key, value)
d will be :
{'A': {'B': {'E': None, 'F': {'G': None, 'H': None}, 'C': {'E': None}}, 'C': None, 'D': None}}
You can use recursion:
d = [{'A': ['B', 'C', 'D']}, {'B': ['E', 'F']}, {'C': ['E']}, {'F': ['G', 'H']}]
d1 = dict(list(i.items())[0] for i in d)
def flatten_structure(d, root = 'A'):
if root:
return {root:flatten_structure(d1[root], None)}
return {i:flatten_structure(d1[i], None) if i in d1 else None for i in d}
print(flatten_structure(d1['A']))
Output:
{'A': {'B': {'E': None, 'F': {'G': None, 'H': None}}, 'C': {'E': None}, 'D': None}}
Related
I have two lists of dictionaries. The first list will contain significantly more dictionaries than the second list. There could be up to 200-300 dictionaries in list1 and no more than 10-15 dictionaries in list2.
For example, any dictionary in list1 that has the same 'g': h key/value as that of list2 needs to add key/value 'j': k to list 1.
list1 = [{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h},
{'a': b, 'c': d, 'e': f, 'g': h}
]
list2 = [{'g': h, 'j': k}]
I'm struggling on finding any previous examples of this type and cannot figure out a function of my own.
A trivial implementation could be:
for d1 in list1:
for d2 in list2:
if any(pair in d1.items() for pair in d2.items()):
d1.update(d2)
The value of list1 after this transformation:
[{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'},
{'a': 'b', 'c': 'd', 'e': 'f', 'g': 'h', 'j': 'k'}]
with a list like below that has one or more dicts
l = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'} } ]
need to extract each key-value pair as an individual dict
Expected output
[{'b': 'h'}, {'c': (1,2)}, {'d': [0, 1]}, {'e': {'f': 2, 'g': 'i'} } ]
I have been trying to do this via list comprehension - the outer comprehension could be something like [ {k,v} for k, v in ?? - need some help in getting the inner comprehension.
I believe this is what you're looking for - except that the order of the elements might be different, but that's to be expected when dealing with dictionaries:
lst = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'}}]
[{k: v} for d in lst for k, v in d.items()]
=> [{'c': (1, 2)}, {'b': 'h'}, {'e': {'g': 'i', 'f': 2}}, {'d': [0, 1]}]
This should work:
[{k: v} for i in l for k, v in i.items()]
def recursion(input_type):
print('input_type ',input_type)
if isinstance(input_type, dict):
num = 0
for k,v in input_type.items():
if isinstance(v, dict):
print('from recursion')
recursion(v)
elif isinstance(v, list):
for j in v:
if isinstance(j, dict):
print('from recursion level 2')
recursion(j)
else:
temp_dict = {k:v}
print('type: ',type(temp_dict), k, v)
print('num',num)
num = num+1
for i in list_:
recursion(i)
How to get the interim results from the recursion.
consider the input as shown below:
input: [{'a':a, 'b':b, 'c':[{'d':d, 'e':e}]}]
Updated input: [ {'a':a, 'b':b, 'c': { 'd':d, 'e': [ {'f':f, 'g':g}, {'f':f1, 'g':g1} ] } } ]
desired output: [{'a':a, 'b':b, 'd':d, 'f':f, 'g':g, 'f_new':f1, 'g_new':g1}]
If the key is duplicate then it should update such as 'f' to 'f_new' or something like that
Thank you in advance!!
You can iterate over the dict items and if an item is a list, recursively flatten the dicts within it:
def f(o):
return {a: b for k, v in o.items() for a, b in ((i for d in (v if isinstance(v, list)
else (v,)) for i in f(d).items()) if isinstance(v, (list, dict)) else ((k, v),))}
so that given:
lst = [{'a': 'a', 'b': 'b', 'c': [{'d': 'd', 'e': 'e'}, {'f': [{'g': 'g'}]}]}]
[f(d) for d in lst] would return:
[{'a': 'a', 'b': 'b', 'd': 'd', 'e': 'e', 'g': 'g'}]
and that given:
lst = [{'a': 'a', 'b': 'b', 'c': {'d': 'd', 'e': {'f': 'f', 'g': 'g'}}}]
[f(d) for d in lst] would return:
[{'a': 'a', 'b': 'b', 'd': 'd', 'f': 'f', 'g': 'g'}]
To avoid collisions in merged keys, append _new to a duplicating key until it is found not pre-existing, in which case you cannot use comprehension:
def f(o):
output = {}
for k, v in o.items():
for a, b in ((i for d in (v if isinstance(v, list) else (v,)) for i in f(d).items())
if isinstance(v, (list, dict)) else ((k, v),)):
while a in output:
a += '_new'
output[a] = b
return output
so that given:
lst = [{'a': 'a', 'b': 'b', 'c': {'d': 'd', 'e': [{'f': 'f', 'g': 'g'}, {'f': 'f1', 'g': 'g1'}]}}]
[f(d) for d in lst] would return:
[{'a': 'a', 'b': 'b', 'd': 'd', 'f': 'f', 'g': 'g', 'f_new': 'f1', 'g_new': 'g1'}]
Demo: https://repl.it/#blhsing/NonstopSeveralActionscript
let's say I have two dictionaries
dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':f}
dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
I'd like a function that returns the second dictionary as
dict_2 ={'A': 'a', 'G': None, 'H': None, 'I': None,'L': None}
which is matching the keys of dict_1 against those in dict_2. If one matches replace the value in dict_2 with the value in dict_1 for that key. Otherwise nothing.
A simple way to do this by iterating over dict_2's items and using dict_1.get() providing default value as dict_2 corresponding value -
>>> dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':f}
>>> dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
>>> for k,v in dict_2.items():
... dict_2[k] = dict_1.get(k,v)
...
>>> dict_2
{'G': None, 'H': None, 'I': None, 'L': None, 'A': 'a'}
Same using dict comprehension -
>>> dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':f}
>>> dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
>>> dict_2 = {k:dict_1.get(k,v) for k,v in dict_2.items()}
>>> dict_2
{'G': None, 'H': None, 'I': None, 'L': None, 'A': 'a'}
Another way is to find the common keys, and iterate over them like this:
dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':f}
dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
for key in set(dict_1.iterkeys()) & set(dict_2.iterkeys()):
dict_2[key] = dict_1[key]
This should be much less computational expensive if it's relatively few common entries compared to the total number of entries in the dictionaries.
You could use dict comprehension and if else to do it :
dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':f}
dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
dict_2={a : dict_1[a] if a in dict_1 else dict_2[a] for a in dict_2.keys() }
print dict_2
output:
{'A': 'a', 'H': None, 'I': None, 'L': None, 'G': None}
But this creates a new dict object
You can use dict.viewkeys to find the common keys:
dict_1 ={'A': 'a', 'B':'b', 'C': 'c', 'D':'d', 'E':'e','F':'f'}
dict_2 ={'A': None, 'G': None, 'H': None, 'I': None,'L': None}
for k in dict_1.viewkeys() & dict_2.viewkeys():
dict_2[k] = dict_1[k]
print(dict_2)
{'A': 'a', 'H': None, 'I': None, 'L': None, 'G': None}
For python3 just use .keys as it returns a dictionary-view-object not a list:
for k in dict_1.keys() & dict_2.keys():
dict_2[k] = dict_1[k]
print(dict_2)
I have a multidictionary:
{'a': {'b': {'c': {'d': '2'}}},
'b': {'b': {'c': {'d': '7'}}},
'c': {'b': {'c': {'d': '3'}}},
'f': {'d': {'c': {'d': '1'}}}}
How can I sort it based on the values '2' '3' '7' '1'
so my output will be:
f.d.c.d.1
a.b.c.d.2
c.b.c.d.3
b.b.c.d.7
You've got a fixed-shape structure, which is pretty simple to sort:
>>> d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'d': {'c': {'d': '1'}}}}
>>> sorted(d, key=lambda x: d[x].values()[0].values()[0].values()[0])
['f', 'a', 'c', 'b']
>>> sorted(d.items(), key=lambda x: x[1].values()[0].values()[0].values()[0])
[('f', {'d': {'c': {'d': '1'}}}),
('a', {'b': {'c': {'d': '2'}}}),
('c', {'b': {'c': {'d': '3'}}}),
('b', {'b': {'c': {'d': '7'}}})]
Yes, this is a bit ugly and clumsy, but only because your structure is inherently ugly and clumsy.
In fact, other than the fact that d['f'] has a key 'd' instead of 'b', it's even more straightforward. I suspect that may be a typo, in which case things are even easier:
>>> d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'b': {'c': {'d': '1'}}}}
>>> sorted(d.items(), key=lambda x:x[1]['b']['c']['d'])
[('f', {'b': {'c': {'d': '1'}}}),
('a', {'b': {'c': {'d': '2'}}}),
('c', {'b': {'c': {'d': '3'}}}),
('b', {'b': {'c': {'d': '7'}}})]
As others have pointed out, this is almost certainly not the right data structure for whatever it is you're trying to do. But, if it is, this is how to deal with it.
PS, it's confusing to call this a "multidictionary". That term usually means "dictionary with potentially multiple values per key" (a concept which in Python you'd probably implement as a defaultdict with list or set as its default). A single, single-valued dictionary that happens to contain dictionaries is better named a "nested dictionary".
In my opinion this kind of design is very hard to read and maintain. Can you consider replacing the internal dictionaries with string-names?
E.g.:
mydict = {
'a.b.c.d' : 2,
'b.b.c.d' : 7,
'c.b.c.d' : 3,
'f.d.c.d' : 1,
}
This one is much easier to sort and waaaay more readable.
Now, a dictionary is something unsortable due to its nature. Thus, you have to sort an e.g. a list representation of it:
my_sorted_dict_as_list = sorted(mydict.items(),
key=lambda kv_pair: kv_pair[1])
you can do it recursively:
d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'d': {'c': {'d': '1'}}}}
def nested_to_string(item):
if hasattr(item, 'items'):
out = ''
for key in item.keys():
out += '%s.' % key + nested_to_string(item[key])
return out
else:
return item + '\n'
print nested_to_string(d)
or
def nested_to_string(item):
def rec_fun(item, temp, res):
if hasattr(item, 'items'):
for key in item.keys():
temp += '%s.' % key
rec_fun(item[key], temp, res)
temp = ''
else:
res.append(temp + item)
res = []
rec_fun(d, '', res)
return res
why do you want to do this.
Your data structure is basically a multi-level tree, so a good way to do what you want is to do what is called a depth-first traversal of it, which can be done recursively, and then massage the intermediate results a bit to sort and format them them into the desired format.
multidict = {'a': {'b': {'c': {'d': '2'}}},
'b': {'b': {'c': {'d': '7'}}},
'c': {'b': {'c': {'d': '3'}}},
'f': {'d': {'c': {'d': '1'}}}}
def nested_dict_to_string(nested_dict):
chains = []
for key,value in nested_dict.items():
chains.append([key] + visit(value))
chains = ['.'.join(chain) for chain in sorted(chains, key=lambda chain: chain[-1])]
return '\n'.join(chains)
def visit(node):
result = []
try:
for key,value in node.items():
result += [key] + visit(value)
except AttributeError:
result = [node]
return result
print nested_dict_to_string(multidict)
Output:
f.d.c.d.1
a.b.c.d.2
c.b.c.d.3
b.b.c.d.7