I receive from another script a dictionary containing various types, in particular other dictionaries or lists that might contain other dictionaries as values.
Now what I want to do is create a single flat dictionary. Keys might be present multiple times within the encapsulated dictionaries. For me the inner most key holds the newest information, so I think dict.update is the right routine to apply when digesting a 'inner' dict. By 'inner' dict I mean a dictionary withing some value of the outermost dictionary.
Now, I understand how to flatten a dictionary by 1 level. What I struggle with to to flatten it by arbitrarily many levels.
A simple example example of the type of dictionary I'm dealing with is:
d = {1: {6: {7: {2: {'a'}}}}, 2: 'b', 3: {4: {2: 'c'}}, 5: ['a', 'b', {1: 'a'}]}
My attempt works ok for a single level of depth:
dd = dict()
for k, v in d.items():
if isinstance(v, dict):
dd.update(v)
elif isinstance(v, list):
for el in v:
if isinstance(el, dict):
dd.update(el)
dd[k] = [el for el in v if not isinstance(el, dict)]
else:
dd[k] = v
This gives me:
Out[56]: {6: {7: {2: {'a'}}}, 2: 'b', 4: {2: 'c'}, 1: 'a', 5: ['a', 'b']}
What it should give is:
{2: 'a', 5: ['a', 'b']}
Note the value of the key 2: 'c' and not (as I get now) 'b'. This should be because the inner-most value for the key 2 is 'c' and not 'b'.
I'm not just looking to get a functioning code (although this would allow me to continue working) but I'd like to understand how such a problem is tackled in python. I have to admit that I'm a little lost here...
Any help is greatly appreciated!
You can use recursion with a generator and keep a counter to determine the depth:
d = {1: {6: {7: {2: {'a'}}}}, 2: 'b', 3: {4: {2: 'c'}}, 5: ['a', 'b', {1: 'a'}]}
def flatten(_d, _depth = 0):
for a, b in _d.items():
if isinstance(b, list):
yield [a, [i for i in b if not isinstance(i, dict)], _depth]
for c in b:
if isinstance(c, dict):
yield from flatten(c, _depth+1)
elif isinstance(b, dict):
yield from flatten(b, _depth+1)
else:
yield [a, b, _depth]
_result = {}
for a, b, c in flatten(d):
if a not in _result:
_result[a] = [b, c]
else:
if _result[a][-1] < c:
_result[a] = [b, c]
print({a:b for a, [b, c] in _result.items()})
Output:
{2: {'a'}, 5: ['a', 'b'], 1: 'a'}
Your approach is correct. But you have update the dict recursively for it to work on any number of levels
def flatten(d):
dd = dict()
for k, v in d.items():
if isinstance(v, dict):
dd.update(flatten(v))
elif isinstance(v, list):
for el in v:
if isinstance(el, dict):
dd.update(flatten(el))
dd[k] = [el for el in v if not isinstance(el, dict)]
else:
dd[k] = v
return dd
d = {1: {2: {'a'}}, 2: 'b', 3: {4: {2: 'c'}}, 5: ['a', 'b', {1: 'a'}]}
print flatten(d)
# {2: 'c', 1: 'a', 5: ['a', 'b']}
Related
I want to convert my dictionary to a triply ordered reversed dictionary.
my dictionary is {1: 1, 3: 3, 5: 6, 6: 6}
I have already tried using nested dictionaries, but i am not sure how to get the required output.
Dict1 = list(Dict)
Dict2['Dict1'] = Dict
print(Dict2)
The output should be {1: {1: [1], 3: [3]}, 2: {6: [5, 6]}}
if i define a different dictionary, i should get different triply ordered dictionary.
Can someone please help me with this?
from collections import defaultdict
orig_d = {1: 1, 3: 3, 5: 6, 6: 6}
new_d = defaultdict(list)
for k, v in orig_d.items():
new_d[v].append(k)
final_d = defaultdict(dict)
for cnt, (k, v) in sorted((len(v), (k, v)) for k, v in new_d.items()):
final_d[cnt][k] = v
print(dict(final_d))
Prints:
{1: {1: [1], 3: [3]}, 2: {6: [5, 6]}}
This question already has answers here:
Filter dict to contain only certain keys?
(22 answers)
Closed 5 years ago.
Consider that I have a dictionary that looks like this:
{1=>a, 2=>b, 3=>c, 4=>d}
and a list that looks like this:
[1, 2, 3]
is there a method that'd return me a subdictionary only containing
{1=>a, 2=>b, 3=>c}
a regular dict-comprehension would do that:
d = {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
keys = [1, 2, 3]
dct = {key: d[key] for key in keys}
print(dct) # {1: 'a', 2: 'b', 3: 'c'}
there are 2 ways to handle keys in keys that are not in the original dictionary:
keys = [1, 2, 3, 7]
# default value None
dct = {key: d[key] if key in d else None for key in keys}
print(dct) # {1: 'a', 2: 'b', 3: 'c', 7: None}
# ignore the key if it is not in the original dict
dct = {key: d[key] for key in set(keys).intersection(d.keys())}
print(dct) # {1: 'a', 2: 'b', 3: 'c'}
I have a dictionary of values that gives the number of occurrences of a value in a list. How can I return a new dictionary that divides the former dictionary into separate dictionaries based on the value?
In other words, I want to sort this dictionary:
>>> a = {'A':2, 'B':3, 'C':4, 'D':2, 'E':3}
to this one.
b = {2: {'A', 'D'}, 3: {'B', 'E'}, 4: {'C'}}
How do I approach the problem?
from collections import defaultdict
a = {'A': 2, 'B': 3, 'C': 4, 'D': 2, 'E': 3}
b = defaultdict(set)
for k, v in a.items():
b[v].add(k)
This is what you'll get:
defaultdict(<class 'set'>, {2: {'D', 'A'}, 3: {'B', 'E'}, 4: {'C'}})
You can convert b to a normal dict afterwards with b = dict(b).
if you are a python beginner like me, you probably wanna try this
a = {'A': 2 , 'B': 3 , 'C' : 4 , 'D' : 2, 'E' : 3}
b = {}
for key in a:
lst = []
new_key = a[key]
if new_key not in b:
lst.append(key)
b[new_key] = lst
else:
b[new_key].append(key)
print(b)
It uses the mutable property of python dictionary to achieve the result you want.
I have a dictionary and want to remove certain values in bad_list from its value list, and return the remainder. Here is the code:
d = {1: ['a', 'c', 'd'], 2: ['b'], 5: ['e']}
bad_list = ['d','e']
ad = {k:d[k].remove(i) for k in d.keys() for sublist in d[k] for i in sublist if i in bad_list}
print 'd =', d
print 'ad =', ad
Unfortunately what that does is it changes the values in d permanently, and returns None for values in ad.
d = {1: ['a', 'c'], 2: ['b'], 5: []}
ad = {1: None, 5: None}
How can I get a dictionary that looks like this:
new_dict = {1: ['a','c'], 2:['b']}
without looping through? I have a much larger dictionary to deal with, and I'd like to do it in the most efficient way.
There is no way to do it without loop:
d = dict((key, [x for x in value if x not in bad_list]) for key, value in d.iteritems())
or with filter:
d = dict((key, filter(lambda x: x not in bad_list, d[key])) for key in d)
UPDATE
To exclude empty values:
d = dict((key, list(x)) for key in d for x in [set(d[key]).difference(bad_list)] if x)
Well, you could just use 'list comprehension', this one liner works, thought I find if ugly.
ad = {k:v for k,v in {k:[i for i in v if i not in bad_list] for k,v in d.items()}.items() if v}
I'd better use a for loop.
ad2 = dict()
for k,v in d.items():
_data_ = [item for item in v if item not in bad_list]
if _data_:
ad2[k]=_data_
Output:
print 'd =', d
print 'ad =', ad
print 'ad2=', ad2
>d = {1: ['a', 'c', 'd'], 2: ['b'], 5: ['e']}
>ad = {1: ['a', 'c'], 2: ['b']}
>ad2= {1: ['a', 'c'], 2: ['b']}
The following code written in Python 3.5 appears to do as requested in your question. Minimal change should be required for it to work with Python 2.x instead. Just use print statements instead of functions.
d = {1: ['a', 'c', 'd'], 2: ['b'], 5: ['e']}
bad_list = ['d', 'e']
ad = {a: b for a, b in ((a, [c for c in b if c not in bad_list]) for a, b in d.items()) if b}
print('d =', d)
print('ad =', ad)
So I'm wondering if anyone can help me out with this issue I'm having.
Lets assume I have a dictionary:
d = {1: {2: 3}, 4: 5}
I want to create a dictionary of any contained dictionaries:
wanted_result = {2: 3}
what I am trying is this:
e = {inner_key: d[key][inner_key] for key in d.keys() for inner_key in d[key].keys() if isinstance(d[key], dict)}
However this is causing me to get an error saying that ints don't have keys, which I know, but I thought my conditional would exclude say 4 from my example from being included in the comprehension.
d = {1: {2: 3}, 4: 5, 6: {7: 8}}
s = {k: v for elem in d.values() if type(elem) is dict for k, v in elem.items()}
>> {2: 3, 7: 8}
In this case, I would recommend you a for-loop and the update method:
d = {1: {2: 3}, 4: 5, 6: {7: 8}}
inner_dicts = {}
for val in d.values():
if type(val) is dict:
inner_dicts.update(val)
print inner_dicts
# {2: 3, 7: 8}
My first idea was something like this:
d = {1: {2: 3}, 4: 5, 6: {7: 8}}
generator = (x.items() for x in d.itervalues() if type(x) == dict)
s = dict(sum( generator, list() ))
# s = {2: 3, 7: 8}
But, in order to avoid letting sum() construct a large temporary list of all the items() you may use itertools.chain and iteritems() instead:
# chain "concatenates" iterables
from itertools import chain
d = {1: {2: 3}, 4: 5, 6: {7: 8}}
s = dict(chain( *(x.iteritems() for x in d.itervalues() if type(x) == dict) ))
# s = {2: 3, 7: 8}
You should be aware of edge cases. Especially if identical keys are in the part-dictionaries these will necessarily collapse somehow.
If you also want to support subclasses of dict to be collected from your d.values(), you may indeed want to use isinstance instead of type. See this question for details.
The following list comprehension will return each value that is a dict:
>>> d = {1: {2: 3}, 4: 5}
>>> [d[i] for i in d.keys() if isinstance(d[i],dict)]
[{2: 3}]
d = {1: {2: 3}, 4: 5,'j':{7:8}}
e={y:d[x][y] for x in d if isinstance(d[x],dict) for y in d[x]}
print(e)
{2: 3, 7: 8}
Other way using lambda
(lambda x: {k:v for x in d.values() if type(x) == dict for k,v in x.items()})(d)