Pythonic way to get keys and values from nested dictionaries - python

just wondering, before I start to work on a function. I always like to hear some pythonic solutions.
I am trying to get keys and values from nested dictionaries:
for an example:
a = {'one': {'animal': 'chicken'},
'two': {'fish': {'sea':'shark'}}}
is there any pythonic way to get values from nested dictionary? Like get straight to value of 'fish'?
Thanks in advance

If you want to find all the items with the "fish" key in the nested dictionary, you can modify this answer flatten nested python dictionaries-compressing keys - answer #Imran
import collections
def get_by_key_in_nested_dict(d, key, parent_key='', sep='_'):
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if key==k:
items.append((new_key, v))
if isinstance(v, collections.MutableMapping):
items.extend(get_by_key_in_nested_dict(v, key, new_key, sep).items())
return dict(items)
with,
test = {
'one': {
'animal': 'chicken'
},
'two': {
'fish': {
'sea':'shark',
'fish':0
}
},
'fish':[1,2,3]
}
get_by_key_in_nested_dict(test,"fish")
You get all the items that have the key "fish"
{
'fish': [1, 2, 3],
'two_fish': {'fish': 0, 'sea': 'shark'},
'two_fish_fish': 0
}

Related

Python Creating new dict from specific keys in other dict (nested)

(Please note I searched and couldn't find an answer for this type of nested, with dict and lists, and with keeping keys names and values).
I'm trying to create a new dict from existing dict with specific keys-value pairs that I need.
Example/origin dict:
{
"test1":{
"test2":[
]
},
"test3":[
],
"test4":{
"test5":0,
"what":{
"in":"2",
"out":"4"
}
},
"test12":[
{
"in2":"a",
"out2":"b"
},
{
"in2":"a33",
"out2":"b33"
}
],
"test9":255
}
I want to select keys for example: ['test1'], ['test4'], ['test12']['in2']
in such way that the result dict will be:
{
"test1":{
"test2":[
]
},
"test4":{
"test5":0,
"what":{
"in":"2",
"out":"4"
}
},
"test12":[
{
"in2":"a"
},
{
"in2":"a33"
}
]
}
I'm aware its possible to do manually, i want to see the pythonic way :)
Thanks!!!
Try a dictionary comprehension with isinstance list:
>>> {k: ([{'in2': i['in2']} for i in v] if isinstance(v, list) else v) for k, v in dct.items() if not isinstance(v, int) and v}
{'test1': {'test2': []},
'test4': {'test5': 0, 'what': {'in': '2', 'out': '4'}},
'test12': [{'in2': 'a'}, {'in2': 'a33'}]}
>>>
I don't think there is one "pythonic" way to do what you want here as there is an infinite number of possible values for your nested dict.
Here is a start of answer that you can adapt to your need !
import copy
def _transform(source_dict: dict, keys_to_keep: list):
dict_copy = copy.deepcopy(source_dict) # no side-effects
for key, value in source_dict.items():
if key not in keys_to_keep:
dict_copy.pop(key)
elif isinstance(value, dict):
dict_copy[key] = _transform(value, keys_to_keep)
elif isinstance(value, list):
dict_copy[key] = [
_transform(el, keys_to_keep) if isinstance(el, dict) else el for el in value
]
return dict_copy

Recursive convert values to string using dictionary comprehension

Using dictionary comprehension is it possible to convert all values recursively to string?
I have this dictionary
d = {
"root": {
"a": "1",
"b": 2,
"c": 3,
"d": 4
}
}
I tried
{k: str(v) for k, v in d.items()}
But the code above turns the entire root value into string and I want this:
d = {"root": {"a": "1", "b": "2", "c": "3", "d": "4"}}
This is not a dictionary comprehension, but it works, it's just one line, and it's recursive!
(f := lambda d: {k: f(v) for k, v in d.items()} if type(d) == dict else str(d))(d)
It only works with Python 3.8+ though (because of the use of an assignment expression).
You could do a recursive solution for arbitrarily nested dicts, but if you only have 2 levels the following is sufficient:
{k: {k2: str(v2) for k2, v2 in v.items()} for k, v in d.items()}
Assuming that your given input was wrong and root's value was a dictionary, your code would somewhat work. You just need to add d['root'].items()
newDict = {k:{k: str(v) for k, v in d[k].items()} for k,v in d.items()}
output
{'root': {'a': '1', 'b': '2', 'c': '3', 'd': '4'}}
The following solution might not be using dictionary comprehension, but it is recursive and can transform dictionaries of any depth, I don't think that's possible using comprehension alone:
def convert_to_string(d):
for key, value in d.items():
if isinstance(value, dict):
convert_to_string(value)
else:
d[key] = str(value)
Found a simpler way to to achieve this using json module. Just made the following
import json
string_json = json.dumps(d) # Convert to json string
d = json.loads(string_json, parse_int=str) # This convert the `int` to `str` recursively.
Using a function
def dictionary_string(dictionary: dict) -> dict:
return json.loads(json.dumps(dictionary), parse_int=str, parse_float=str)
Regards

Add missing keys to `dict` in `list`

So, I need to plot something with matplotlib, and all was going perfect up until this error. Basically, what I need is to turn the following:
[
{
"foo":1,
"bar":2
},
{
"foo":8,
"baz":3
}
]
into something like this:
[
{
"foo":1,
"bar":2,
"baz":0
},
{
"foo":8,
"baz":3,
"bar":0
}
]
Is there any way this can be done?
One way is to calculate the union of your keys and then iterate via a for loop.
keys = set().union(*lst)
for d in lst:
for k in keys - d.keys():
d[k] = 0
print(lst)
[{'foo': 1, 'bar': 2, 'baz': 0},
{'foo': 8, 'baz': 3, 'bar': 0}]
Since we're posting answers... There's an easier way. Simply turn your dicts into defaultdicts wherever it is that you're defining them. - defaultdict(int, your_dictionary)
You can then access this dict for your plot regardless of non-existent keys since if they don't exist the defaultdict will just return 0 here as you want. No point reinventing the wheel here.
If keys is the list of keys and L is the list of dictionaries then:
for d in L:
for k in keys:
d.setdefault(k, 0)
sets each missing key to 0.

make a dict/json from string with duplicate keys Python

I have a string that could be parsed as a JSON or dict object. My string variable looks like this :
my_string_variable = """{
"a":1,
"b":{
"b1":1,
"b2":2
},
"b": {
"b1":3,
"b2":2,
"b4":8
}
}"""
When I do json.loads(my_string_variable), I have a dict but only the second value of the key "b" is kept, which is normal because a dict can't contain duplicate keys.
What would be the best way to have some sort of defaultdict like this :
result = {
"a": 1,
"b": [{"b1": 1, "b2": 2}, {"b1": 3, "b2": 2, "b4": 8}],
}
I have already looked for similar questions but they all deal with dicts or lists as an input and then create defaultdicts to handle the duplicate keys.
In my case I have a string variable and I would want to know if there is a simple way to achieve this.
something like the following can be done.
import json
def join_duplicate_keys(ordered_pairs):
d = {}
for k, v in ordered_pairs:
if k in d:
if type(d[k]) == list:
d[k].append(v)
else:
newlist = []
newlist.append(d[k])
newlist.append(v)
d[k] = newlist
else:
d[k] = v
return d
raw_post_data = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
newdict = json.loads(raw_post_data, object_pairs_hook=join_duplicate_keys)
print (newdict)
Please note that above code depends on value type, if type(d[k]) == list. So if original string itself gives a list then there could be some error handling required to make the code robust.
Accepted answer is perfectly fine. I just wanted to show another approach.
So at first, you dedicate a list for values in order to easily accumulate next values. At the end, you call pop on the lists which have only one item. This means that the list doesn't have duplicate values:
import json
from collections import defaultdict
my_string_variable = '{"a":1, "b":{"b1":1,"b2":2}, "b": { "b1":3, "b2":2,"b4":8} }'
def join_duplicate_keys(ordered_pairs):
d = defaultdict(list)
for k, v in ordered_pairs:
d[k].append(v)
return {k: v.pop() if len(v) == 1 else v for k, v in d.items()}
d = json.loads(my_string_variable, object_pairs_hook=join_duplicate_keys)
print(d)
output:
{'a': 1, 'b': [{'b1': 1, 'b2': 2}, {'b1': 3, 'b2': 2, 'b4': 8}]}

How to delete entries in a dictionary with a given flag in python?

I have a dictionary, lets call it myDict, in Python that contains a set of similar dictionaries which all have the entry "turned_on : True" or "turned_on : False". I want to remove all the entries in myDict that are off, e.g. where "turned_on : False". In Ruby I would do something like this:
myDict.delete_if { |id,dict| not dict[:turned_on] }
How should I do this in Python?
You mean like this?
myDict = {"id1" : {"turned_on": True}, "id2" : {"turned_on": False}}
result = dict((a, b) for a, b in myDict.items() if b["turned_on"])
output:
{'id1': {'turned_on': True}}
Straight-forward way:
def delete_if_not(predicate_key, some_dict):
for key, subdict in some_dict.items():
if not subdict.get(predicate_key, True):
del some_dict[key]
Testing:
mydict = {
'test1': {
'turned_on': True,
'other_data': 'foo',
},
'test2': {
'turned_on': False,
'other_data': 'bar',
},
}
delete_if_not('turned_on', mydict)
print mydict
The other answers on this page so far create another dict. They don't delete the keys in your actual dict.
It's not clear what you want, but my guess is:
myDict = {i: j for i, j in myDict.items() if j['turned_on']}
or for older version of python:
myDict = dict((i, j) for i, j in myDict.iteritems() if j['turned_on'])
d = { 'id1':{'turned_on':True}, 'id2':{'turned_on':False}}
dict((i,j) for i, j in d.items() if not j['turned_on'])

Categories

Resources