Remove all empty keys in a nested dictionary (via yield) - python

I have the following function to remove keys with an empty value or that start with an underscore. It works on a non-nested dictionary:
def _remove_missing_and_underscored_keys(d):
if not d: return d
for key in d.keys():
if not d.get(key):
del d[key]
elif key.startswith('_'):
del d[key]
return d
d = {"Name": "David",
"_Age": 50,
"Numbers": [1,2,3,4,5],
"Height": ""
}
>>> _remove_missing_and_underscored_keys(d)
{'Name': 'David', 'Numbers': [1, 2, 3, 4, 5]}
However, I would like to create the above so that it can remove nested items as well. I believe I need to use a yield statement for this, but I'm not having luck with implementing it properly. Here is an example of what I want it to do:
d = {
"PlatformID": "B00EU7XL9Q",
"Platform": "Amazon",
"Type": "Collection",
"Products": {
"UK": {
"URL": "http://www.amazon.co.uk/dp/B00EU7XL9Q",
"Rating": None,
"_IsAudited": True,
"Offers": {
"HDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
},
"SDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
}
}
}
}
}
>>> _remove_missing_and_underscored_keys(d)
{
"PlatformID": "B00EU7XL9Q",
"Platform": "Amazon",
"Type": "Collection",
"Products": {
"UK": {
"URL": "http://www.amazon.co.uk/dp/B00EU7XL9Q",
"Offers": {
"HDBUY": {
"Currency": "GBP",
"Cost": "14.99",
},
"SDBUY": {
"Currency": "GBP",
"Cost": "14.99",
}
}
}
}
}
In other words, it will do the above operation on all nested levels of the dict.

You can use recursion with a dictionary comprehension:
d = {'PlatformID': 'B00EU7XL9Q', 'Platform': 'Amazon', 'Type': 'Collection', 'Products': {'UK': {'URL': 'http://www.amazon.co.uk/dp/B00EU7XL9Q', 'Rating': None, '_IsAudited': True, 'Offers': {'HDBUY': {'Currency': 'GBP', 'FutureReleaseStartDate': None, 'Cost': '14.99', 'IsFutureRelease': False}, 'SDBUY': {'Currency': 'GBP', 'FutureReleaseStartDate': None, 'Cost': '14.99', 'IsFutureRelease': False}}}}}
def _del(_d):
return {a:_del(b) if isinstance(b, dict) else b for a, b in _d.items() if b and not a.startswith('_')}
import json
print(json.dumps(_del(d), indent=4))
Output:
{
"PlatformID": "B00EU7XL9Q",
"Platform": "Amazon",
"Type": "Collection",
"Products": {
"UK": {
"URL": "http://www.amazon.co.uk/dp/B00EU7XL9Q",
"Offers": {
"HDBUY": {
"Currency": "GBP",
"Cost": "14.99"
},
"SDBUY": {
"Currency": "GBP",
"Cost": "14.99"
}
}
}
}
}

def _remove_missing_and_underscored_keys(d):
if not d: return d
for key in d.keys():
if not d.get(key):
del d[key]
elif key.startswith('_'):
del d[key]
elif type(d[key]) == dict:
d[key] = _remove_missing_and_underscored_keys(d[key])
return d

I think you meant to say, you needed to use recursion to solve this. I don't think using a generator quite solves your problem.
Another caveat is that you shouldn't iterate through a variable you are changing. That's why I create the copy _d modify and return that and iterate through the original structure.
import pprint
def _remove_missing_and_underscored_keys(d):
if not d: return d
_d = d.copy()
for key in _d.keys():
if not _d.get(key):
del d[key]
elif key.startswith('_'):
del d[key]
elif isinstance(_d[key], dict):
_remove_missing_and_underscored_keys(_d[key])
return _d
_d = {
"PlatformID": "B00EU7XL9Q",
"Platform": "Amazon",
"Type": "Collection",
"Products": {
"UK": {
"URL": "http://www.amazon.co.uk/dp/B00EU7XL9Q",
"Rating": None,
"_IsAudited": True,
"Offers": {
"HDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
},
"SDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
}
}
}
}
}
foo = _remove_missing_and_underscored_keys(_d)
pprint.pprint(foo)
Output:
{'Platform': 'Amazon',
'PlatformID': 'B00EU7XL9Q',
'Products': {'UK': {'Offers': {'HDBUY': {'Cost': '14.99', 'Currency': 'GBP'},
'SDBUY': {'Cost': '14.99', 'Currency': 'GBP'}},
'URL': 'http://www.amazon.co.uk/dp/B00EU7XL9Q'}},
'Type': 'Collection'}

Just go recursive.
Add another check to see if a value in primary dict is a dict, and call the same function on it.
# your code goes here
def _remove_missing_and_underscored_keys(d):
if not d: return d
for key in d.keys():
if not d.get(key):
del d[key]
elif key.startswith('_'):
del d[key]
elif type(d[key]) is dict:
#print("key '{}' stores a dict '{}', need to cleanup recursively".format(key, d[key]))
d[key] = _remove_missing_and_underscored_keys(d[key])
# Keep below check if you want to treat empty dict as `empty` as well
if d[key] == None or d[key] == {}:
del d[key]
return d
d = {
"PlatformID": "B00EU7XL9Q",
"Platform": "Amazon",
"Type": "Collection",
"Products": {
"UK": {
"URL": "http://www.amazon.co.uk/dp/B00EU7XL9Q",
"Rating": None,
"_IsAudited": True,
"Offers": {
"HDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
},
"SDBUY": {
"Currency": "GBP",
"FutureReleaseStartDate": None,
"Cost": "14.99",
"IsFutureRelease": False
},
"x" : {
"y":None
}
}
}
}
}
e = _remove_missing_and_underscored_keys(d)
print(e)
See it in action: https://ideone.com/5xDDZl
Above code also handles empty dicts stored at any key or any dict that became empty after recursively cleaning it. You can remove that check if needed.

Related

How to update values in a nested dictionary?

I have 2 dictionaries:
data = {
"filter":
{
"and":
[
{
"or":
[
{
"and":
[
{"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
{"category": "skill", "key": "26366", "value": 100, "operator": "EQ"},
],
},
],
},
{"or": [{"category": "skill", "key": "45165", "operator": "NE"}]},
{"or": [{"category": "skill", "key": "48834", "value": 80, "operator": "GT"}]},
{"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
],
},
}
new_val = {'26366': '11616', '45165': '11613', '48834': '11618'}
I want to update values in "data" dictionary with the values from "new_val" dictionary.
So that 26366(in "data" dict) becomes 11616(from "new_val" dict), 45165 becomes 11613, and 48834 becomes 11618.
"data" dictionary nesting can be different (both up and down)
The key in the "data" dictionary can be different, not only "key", it can be "skill_id", "filter_id" and so on.
And get this result:
{
"filter":
{
"and":
[
{
"or":
[
{
"and":
[
{"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
{"category": "skill", "key": "11616", "value": 100, "operator": "EQ"},
],
},
],
},
{"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
{"or": [{"category": "skill", "key": "11618", "value": 80, "operator": "GT"}]},
{"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
],
},
}
To return an updated dict without modifying the old one:
def updated_in_depth(d, replace):
if isinstance(d, dict):
return {k: updated_in_depth(v, replace)
for k,v in d.items()}
elif isinstance(d, list):
return [updated_in_depth(x, replace) for x in d]
else:
return replace.get(d, d)
Testing with your data and new_val:
>>> updated_in_depth(data, new_val)
{'filter': {'and': [{'or': [{'and': [
{'category': 'profile', 'key': 'languages', 'operator': 'IN', 'value': 'EN'},
{'category': 'skill', 'key': '11616', 'value': 100, 'operator': 'EQ'}]}]},
{'or': [{'category': 'skill', 'key': '11613', 'operator': 'NE'}]},
{'or': [{'category': 'skill', 'key': '11618', 'value': 80, 'operator': 'GT'}]},
{'or': [{'category': 'profile', 'key': 'gender', 'operator': 'EQ', 'value': 'FEMALE'}]}]}}
Use something like this:
data['filter']['and']['or']['and'][1]['key']='11616'
To search for the keys recursively you can do:
from copy import deepcopy
def replace(d, new_vals):
if isinstance(d, dict):
# replace key (if there's match):
if "key" in d:
d["key"] = new_vals.get(d["key"], d["key"])
for v in d.values():
replace(v, new_vals)
elif isinstance(d, list):
for v in d:
replace(v, new_vals)
new_data = deepcopy(data)
replace(new_data, new_val)
print(new_data)
Prints:
{
"filter": {
"and": [
{
"or": [
{
"and": [
{
"category": "profile",
"key": "languages",
"operator": "IN",
"value": "EN",
},
{
"category": "skill",
"key": "11616",
"value": 100,
"operator": "EQ",
},
]
}
]
},
{"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
{
"or": [
{
"category": "skill",
"key": "11618",
"value": 80,
"operator": "GT",
}
]
},
{
"or": [
{
"category": "profile",
"key": "gender",
"operator": "EQ",
"value": "FEMALE",
}
]
},
]
}
}
If you don't need copy of data you can omit the deepcopy:
replace(data, new_val)
print(data)
You can build a recursive function like this
def walk_dict(d):
if isinstance(d, list):
for item in d:
walk_dict(item)
elif isinstance(d, dict):
if 'key' in d and d['key'] in new_val:
d['key'] = new_val[d['key']]
for k, v in d.items():
walk_dict(v)
walk_dict(data)
print(data)
As many have advised, a recursive function will do the trick:
def a(d):
if isinstance(d, dict): # if dictionary, apply a to all values
d = {k: a(d[k]) for k in d.keys()}
return d
elif isinstance(d, list): # if list, apply to all elements
return [a(x) for x in d]
else: # apply to d directly (it is a number, a string or a bool)
return new_val[d] if d in new_val else d
When a is called, it check what is the type of the variable d:
if d is a list, it apply a to each element of the list and return the updated list
if d is a dict, it applies a to all values and return the updated dict
otherwise, it returns the mapped new value if the old one has been found in the new_val keys
data = {
"filter":
{
"and":
[
{
"or":
[
{
"and":
[
{"category": "profile", "key": "languages", "operator": "IN", "value": "EN"},
{"category": "skill", "key": "11616", "value": 100, "operator": "EQ"},
],
},
],
},
{"or": [{"category": "skill", "key": "11613", "operator": "NE"}]},
{"or": [{"category": "skill", "key": "11618", "value": 80, "operator": "GT"}]},
{"or": [{"category": "profile", "key": "gender", "operator": "EQ", "value": "FEMALE"}]},
],
},
}
class Replace:
def __init__(self,data):
self.data=data
def start(self,d):
data = self.data
def replace(data):
if type(data) == list:
for v in data:
replace(v)
if type(data) == dict:
for k,v in data.items():
if type(v) == dict:
replace(v)
if type(v) == str:
if v in d:
data[k] = d[v]
replace(data)
return data
new_data = Replace(data).start({'26366': '11616',
'45165': '11613',
'48834': '11618'})
print(new_data)

Deep python dictionary recursion

I have a python dictionary:
d = {
"config": {
"application": {
"payment": {
"dev": {
"modes": {"credit,debit,emi": {}},
"company": {
"address": {
"city": {"London": {}},
"pincode": {"LD568162": {}},
},
"country": {"United Kingdom": {}},
"phone": {"7865432765": {}},
},
"levels": {"0,1,2": {}},
},
"prod": {"modes": {"credit,debit": {}}, "levels": {"0,1": {}}},
}
}
}
}
I want to change it to something like this(if the value is empty{} then make the key as value for its parent)
d = {
"config": {
"application": {
"payment": {
"dev": {
"modes": "credit,debit,emi",
"company": {
"address": {
"city": "London",
"pincode": "LD568162"
},
"country": "United Kingdom",
"phone": "7865432765"
},
"levels": "0,1,2"
},
"prod": {
"modes": "credit,debit",
"levels": "0,1"
}
}
}
}
}
i tried to write the code to traverse this deep dictionary, but couldn't modify it to get the above output. Please help.
def recur(json_object):
for x in list(json_object.items()):
print(x)
recur(json_object[x])
d={'config': {'application': {'payment': {'dev': {'modes': {'credit,debit,emi': {}}, 'company': {'address': {'city': {'London': {}}, 'pincode': {'LD568162': {}}}, 'country': {'United Kingdom': {}}, 'phone': {'7865432765': {}}}, 'levels': {'0,1,2': {}}}, 'prod': {'modes': {'credit,debit': {}}, 'levels': {'0,1': {}}}}}}}
Solution 1
We can use a non-recursive approach with queues to enqueue each inner/nested element of the document and put as value if the nested value is just {}:
# d = ...
queue = [d]
while queue:
data = queue.pop()
for key, value in data.items():
if isinstance(value, dict) and list(value.values()) == [{}]:
data[key] = list(value.keys())[0]
elif isinstance(value, dict):
queue.append(value)
print(d)
Output
{
"config": {
"application": {
"payment": {
"dev": {
"modes": "credit,debit,emi",
"company": {
"address": {
"city": "London",
"pincode": "LD568162"
},
"country": "United Kingdom",
"phone": "7865432765"
},
"levels": "0,1,2"
},
"prod": {
"modes": "credit,debit",
"levels": "0,1"
}
}
}
}
}
Solution 2
Here's a recursive approach
# d = ...
def recur(data):
for key, value in data.items():
if isinstance(value, dict) and list(value.values()) == [{}]:
data[key] = list(value.keys())[0]
elif isinstance(value, dict):
recur(value)
recur(d)
print(d)
Output
Same as Solution 1

Expanding a dictionary using dot notation

I have the following json document:
{
"id": "5c26321bd8f4113d43b91141",
"idMemberCreator": "5b203bc7e47d817a8138bc37",
"data": {
"list": {
"name": "Sorji for QA",
"id": "5b0a2543b89acdbdb85f7b42"
},
"board": {
"shortLink": "iyCzZ5jx",
"name": "FlicksIO",
"id": "5b0a251f68a9e74b8ec3b3ac"
},
"card": {
"shortLink": "vOt2vO7v",
"idShort": 92,
"name": "New column in main for Storefront provider correlation.",
"id": "5b9c0023533f7c26424ea4ed",
"closed": true
},
"old": {
"closed": false
}
},
"type": "updateCard",
"date": "2018-12-28T14:24:27.455Z",
"limits": {},
"memberCreator": {
"id": "5b203bc7e47d817a8138bc37",
"avatarHash": "73bfa48c76c3c92615fe89ff79a6c5ae",
"avatarUrl": "https://trello-avatars.s3.amazonaws.com/73bfa48f79a6c5ae",
"fullName": "Marie Bond",
"idMemberReferrer": null,
"initials": "MB",
"username": "mb"
}
}
I would like to expand this out to be a single level with dot notation. That is, it should look like:
{
"id": "5c26321bd8f4113d43b91141",
"idMemberCreator": "5b203bc7e47d817a8138bc37",
"data.list.name": "Sorji for QA",
"data.list.id": "5b0a2543b89acdbdb85f7b42"
"data.board.shortLink": "iyCzZ5jx",
"data.board.name": "FlicksIO",
"data.board.id": "5b0a251f68a9e74b8ec3b3ac"
"data.card.shortLink": "vOt2vO7v",
"data.card.idShort": 92,
"data.card.name": "New column in main for Storefront provider correlation.",
"data.card.id": "5b9c0023533f7c26424ea4ed",
"data.card.closed": true
"data.old.closed": false
"type": "updateCard",
"date": "2018-12-28T14:24:27.455Z",
"limits": {},
"memberCreator.id": "5b203bc7e47d817a8138bc37",
"memberCreator.avatarHash": "73bfa48c76c3c92615fe89ff79a6c5ae",
"memberCreator.avatarUrl": "https://trello-avatars.s3.amazonaws.com/73bfa48f79a6c5ae",
"memberCreator.fullName": "Marie Bond",
"memberCreator.idMemberReferrer": null,
"memberCreator.initials": "MB",
"memberCreator.username": "mb"
}
Would it be possible to do this with a generator object? I've been working a lot on recursion today, and have been trying to move from while loops to using generator objects and yields, etc.
You can keep a parameter in the signature of the recursive function to store the paths:
data = {'id': '5c26321bd8f4113d43b91141', 'idMemberCreator': '5b203bc7e47d817a8138bc37', 'data': {'list': {'name': 'Sorji for QA', 'id': '5b0a2543b89acdbdb85f7b42'}, 'board': {'shortLink': 'iyCzZ5jx', 'name': 'FlicksIO', 'id': '5b0a251f68a9e74b8ec3b3ac'}, 'card': {'shortLink': 'vOt2vO7v', 'idShort': 92, 'name': 'New column in main for Storefront provider correlation.', 'id': '5b9c0023533f7c26424ea4ed', 'closed': True}, 'old': {'closed': False}}, 'type': 'updateCard', 'date': '2018-12-28T14:24:27.455Z', 'limits': {}, 'memberCreator': {'id': '5b203bc7e47d817a8138bc37', 'avatarHash': '73bfa48c76c3c92615fe89ff79a6c5ae', 'avatarUrl': 'https://trello-avatars.s3.amazonaws.com/73bfa48f79a6c5ae', 'fullName': 'Marie Bond', 'idMemberReferrer': None, 'initials': 'MB', 'username': 'mb'}}
def dot_paths(d, _paths = []):
for a, b in d.items():
if not b or not isinstance(b, dict):
yield ['.'.join(_paths+[a]), b]
else:
yield from dot_paths(b, _paths+[a])
import json
print(json.dumps(dict(dot_paths(data)), indent=4))
Output:
{
"id": "5c26321bd8f4113d43b91141",
"idMemberCreator": "5b203bc7e47d817a8138bc37",
"data.list.name": "Sorji for QA",
"data.list.id": "5b0a2543b89acdbdb85f7b42",
"data.board.shortLink": "iyCzZ5jx",
"data.board.name": "FlicksIO",
"data.board.id": "5b0a251f68a9e74b8ec3b3ac",
"data.card.shortLink": "vOt2vO7v",
"data.card.idShort": 92,
"data.card.name": "New column in main for Storefront provider correlation.",
"data.card.id": "5b9c0023533f7c26424ea4ed",
"data.card.closed": true,
"data.old.closed": false,
"type": "updateCard",
"date": "2018-12-28T14:24:27.455Z",
"limits": {},
"memberCreator.id": "5b203bc7e47d817a8138bc37",
"memberCreator.avatarHash": "73bfa48c76c3c92615fe89ff79a6c5ae",
"memberCreator.avatarUrl": "https://trello-avatars.s3.amazonaws.com/73bfa48f79a6c5ae",
"memberCreator.fullName": "Marie Bond",
"memberCreator.idMemberReferrer": null,
"memberCreator.initials": "MB",
"memberCreator.username": "mb"
}

Print all keys from a dictionary in the format of key1.key2.key3

Here is my dictionary (or JSON)
{
"$schema": "http://json-schema.org/draft-03/schema#",
"name": "Product",
"type": "object",
"properties": {
"id": {
"type": "number",
"description": "Product identifier",
"required": True
},
"name": {
"type": "string",
"description": "Name of the product",
"required": True
},
"price": {
"type": "number",
"minimum": 0,
"required": True
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"stock": {
"type": "object",
"properties": {
"warehouse": {
"type": "number"
},
"retail": {
"type": "number"
}
}
}
}
}
I want to print all keys with this format key1.key2.key3. This is my code:
def myprint(d, keys = ''):
for k, v in d.items():
temp = keys
keys += k
if isinstance(v,dict):
keys += '.'
myprint(v,keys)
else:
print(keys)
keys = temp
Unfortunately, this failed where returns result like this:
$schema
type
name
properties.stock.type
properties.stock.properties.warehouse.type
properties.stock.properties.warehouse.retail.type
properties.stock.price.minimum
properties.stock.price.type
properties.stock.price.required
properties.stock.price.tags.items.type
properties.stock.price.tags.items.type
properties.stock.price.tags.id.required
properties.stock.price.tags.id.type
properties.stock.price.tags.id.description
properties.stock.price.tags.id.name.required
properties.stock.price.tags.id.name.type
properties.stock.price.tags.id.name.description
As you see, the last several lines are wrong.
Anyone have suggestions? Not only restrict from this script other methods are welcome, but no module being used.
You can use recursion:
d = {'$schema': 'http://json-schema.org/draft-03/schema#', 'name': 'Product', 'type': 'object', 'properties': {'id': {'type': 'number', 'description': 'Product identifier', 'required': True}, 'name': {'type': 'string', 'description': 'Name of the product', 'required': True}, 'price': {'type': 'number', 'minimum': 0, 'required': True}, 'tags': {'type': 'array', 'items': {'type': 'string'}}, 'stock': {'type': 'object', 'properties': {'warehouse': {'type': 'number'}, 'retail': {'type': 'number'}}}}}
def display_keys(s, last=None):
for a, b in s.items():
if not isinstance(b, dict):
yield "{}.{}".format(last, a) if last else str(a)
else:
for h in display_keys(b, str(a) if not last else '{}.{}'.format(last, a)):
yield h
print(list(display_keys(d)))
Output:
['$schema', 'name', 'type', 'properties.id.type', 'properties.id.description', 'properties.id.required', 'properties.name.type', 'properties.name.description', 'properties.name.required', 'properties.price.type', 'properties.price.minimum', 'properties.price.required', 'properties.tags.type', 'properties.tags.items.type', 'properties.stock.type', 'properties.stock.properties.warehouse.type', 'properties.stock.properties.retail.type']
I think you make it complicated by updating keys. A string in Python is immutable. We can each time pass an extended key to the next recursive level, so with:
def myprint(d, keys = ''):
for k, v in d.items():
if isinstance(v, dict):
myprint(v, '{}{}.'.format(keys, k))
else:
print('{}{}'.format(keys, k))
This then yields:
>>> myprint(d)
$schema
name
type
properties.id.type
properties.id.description
properties.id.required
properties.name.type
properties.name.description
properties.name.required
properties.price.type
properties.price.minimum
properties.price.required
properties.tags.type
properties.tags.items.type
properties.stock.type
properties.stock.properties.warehouse.type
properties.stock.properties.retail.type
The problem with your code was that you only restored the old keys value in case something was not a dictionary. So as a result if there were multiply subdictionaries, you started to concatenate the keys together.

Update a dictionary value using python

I have a json file, which when loaded in python using json.loads() becomes a dictionary. The json data is a nested dictionary which can contain a 'groups' key inside another 'groups' key. The values inside the 'groups' key are a 'name' key and a 'properties' key.
Each 'properties' key has a unique 'name' and a 'value' key.
My objective is to search for a 'groups' key having its 'name' key value as "SportCar", which has a properties key having a name key value as "BMW", and only when these conditions are satisfied, update the 'data' key from 'data':value1 to 'data':value2.
An example of the json is as follows
{
"groups": [
{
"name": "SportCar",
"properties": [
{
"name": "BMW",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
},
{
"name": "Audi",
"value": {
"type": "Boolean",
"data": true
}
}
],
"groups": [
{
"name": "Trucks",
"properties": [
{
"name": "Volvo",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
}
]
}
]
},
{
"name": "MotorCycle",
"properties": [
{
"name": "Yamaha",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
}
],
"groups": [
{
"name": "Speeders",
"properties": [
{
"name": "prop2",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
}
]
}
]
}
]
}
The above json is contained in myjson22.json. Here is what I have tried so far:
import json
from pprint import pprint
json_data=open('myjson22.json', 'r')
data = json.load(json_data)
#print(data)
def get_recursively(search_dict, field):
"""
To read the json data as type dict and search all 'groups' keys for the 'name' key value value provided.
"""
fields_found = []
for key, value in search_dict.items():
if key == field:
fields_found.append(value)
elif isinstance(value, dict):
results = get_recursively(value, field)
for result in results:
fields_found.append(result)
elif isinstance(value, list):
for item in value:
if isinstance(item, dict):
more_results = get_recursively(item, field)
for another_result in more_results:
fields_found.append(another_result)
return fields_found
get_recursively(data, ["properties"][0])
and the output was:
[[{'name': 'BMW',
'value': {'data': 'value1', 'encoding': 'utf-8', 'type': 'String'}},
{'name': 'Audi', 'value': {'data': True, 'type': 'Boolean'}}],
[{'name': 'Volvo',
'value': {'data': 'value1', 'encoding': 'utf-8', 'type': 'String'}}],
[{'name': 'Yamaha',
'value': {'data': 'value1', 'encoding': 'utf-8', 'type': 'String'}}],
[{'name': 'prop2',
'value': {'data': 'value1', 'encoding': 'utf-8', 'type': 'String'}}]]
A way to implement this recursive solution is with backtracking. When no more 'groups' keys are are found nested inside the root key, the 'name' key value is matched with groups_name parameter, which is 'SportCar' in our case. If this condition is satisfied check for the values inside same 'groups' key's (i.e. 'SportCar' key's) 'properties' key and match its 'name' key value with the properties_name parameter (which is 'BMW' in our case). If this second condition is also true, then update the 'data' key value inside the same 'properties' key, as per requirements, or else return (for backtracking).
import json
json_data = open('myjson22.json', 'r')
data = json.load(json_data)
def get_recursively( myJson, groups_name, properties_name, value2):
if 'groups' in myJson.keys():
# As there are multiple values inside 'groups' key
for jsonInsideGroupsKey in myJson['groups']:
get_recursively( jsonInsideGroupsKey, groups_name, properties_name, value2)
if 'name' in myJson.keys():
# check for groups name
if myJson['name'] == groups_name:
# check for properties name
if myJson['properties'][0]['name'] == properties_name:
# Update value. The changes will persist as we backtrack because
# we are making the update at the original memory location
# and not on a copy. For more info see deep and shallow copy.
myJson['properties'][0]['value']['data'] = value2
return
get_recursively(data,'SportCar','BMW','changedValue1')
get_recursively(data,'Speeders','prop2','changedValue2')
print data
my output:
{u'groups': [{u'name': u'SportCar', u'groups': [{u'name': u'Trucks', u'properties': [{u'name': u'Volvo', u'value': {u'data': u'value1', u'type': u'String', u'encoding': u'utf-8'}}]}], u'properties': [{u'name': u'BMW', u'value': {u'data': 'changedValue1', u'type': u'String', u'encoding': u'utf-8'}}, {u'name': u'Audi', u'value': {u'data': True, u'type': u'Boolean'}}]}, {u'name': u'MotorCycle', u'groups': [{u'name': u'Speeders', u'properties': [{u'name': u'prop2', u'value': {u'data': 'changedValue2', u'type': u'String', u'encoding': u'utf-8'}}]}], u'properties': [{u'name': u'Yamaha', u'value': {u'data': u'value1', u'type': u'String', u'encoding': u'utf-8'}}]}]}
prettified it will look as:
{
"groups": [
{
"name": "SportCar",
"properties": [
{
"name": "BMW",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "ChangedValue1"
}
},
{
"name": "Audi",
"value": {
"type": "Boolean",
"data": true
}
}
],
"groups": [
{
"name": "Trucks",
"properties": [
{
"name": "Volvo",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
}
]
}
]
},
{
"name": "MotorCycle",
"properties": [
{
"name": "Yamaha",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "value1"
}
}
],
"groups": [
{
"name": "Speeders",
"properties": [
{
"name": "prop2",
"value": {
"type": "String",
"encoding": "utf-8",
"data": "ChangedValue2"
}
}
]
}
]
}
]
}

Categories