So I have a fairly simple set of data, such as:
['test.sh','api.test.sh','blah.api.test.sh','test.com','api.test.com']
and I need to transform into a hierarchical data structure, I was thinking of doing it with a dictionary:
{ 'name':'test.sh',
'children': { 'name':'api.test.sh',
'children': { 'name':'blah.api.test.sh' }
}
},
{
'name':'test.com',
'children': { 'name':'api.test.com' }
}
And essentially for each high level name I can work my way down and perform the operations I need to do.
My question has more to do with creating a simple way to sort, match, and transform the data. I can think of a few ways to do this but I can't think of anything quite elegant. Also I'm doing this in python.
Thanks
I think this could be what you are looking for:
def sort_dns(l):
to_return = []
# Get top-level domains: the domains that contain the less amount of dots.
count_list = [i.count('.') for i in l]
min_dots = min(count_list)
top_domains = [i for i in l if i.count('.') == min_dots]
# Now for each domain, we find it subdomains.
for domain in top_domains:
sub_domains = [i for i in l if domain in i and i is not domain]
#And untill we aren't at the deepest level, we continue looking for sub domains and repeat the structure
sub_sub_domains = sort_dns(sub_domains) if not len(sub_domains) == 0 else None
to_return.append({'name' : domain, 'childrens' : sub_sub_domains})
return to_return
As you see this function call itself recursively to go infinitely "deep" if needed.
With your example, the result is the following
[
{
'name': 'test.sh',
'childrens': [
{
'name': 'api.test.sh',
'childrens': [
{'name': 'blah.api.test.sh', 'childrens': None}
]
}
]
},
{
'name': 'test.com',
'childrens': [
{'name': 'api.test.com', 'childrens': None}
]
}
]
As you see it handle the case of multiple childrens and no children at all.
Note that if you don't want the 'childrens': None, You can change the function to that:
def sort_dns(l):
to_return = []
# Get top-level domains: the domains that contain the less amount of dots.
count_list = [i.count('.') for i in l]
min_dots = min(count_list)
top_domains = [i for i in l if i.count('.') == min_dots]
# Now for each domain, we find it subdomains.
for domain in top_domains:
sub_domains = [i for i in l if domain in i and i is not domain]
#And untill we aren't at the deepest level, we continue looking for sub domains and repeat the structure
sub_sub_domains = sort_dns(sub_domains) if not len(sub_domains) == 0 else None
if sub_sub_domains:
to_return.append({'name' : domain, 'childrens' : sub_sub_domains})
else:
to_return.append({'name' : domain})
return to_return
Note that this is Python3 Code.
EDIT: I've read roippi Answer and this works great too, His solution is surely the most pythonic. This one's advantage is that it doesn't require any imports. But you should really consider roippi answer as the most elegant.
So, I see a proper approach to this problem happening in three steps: sort, group, format.
First, sorting your inputs to arrange them in logical groups. You can define a quick helper function to define your sort key:
def sorter(netloc):
split = netloc.split('.')
return (split[::-1], -len(split))
And use it thusly:
data = ['test.sh','api.test.sh','blah.api.test.sh','test.com','api.test.com', 'another.com', 'sub.another.com', 'sub.sub.another.com']
#shuffling data, to show that sorting works
import random
random.shuffle(data)
sorted(data, key=sorter)
Out[14]:
['another.com',
'sub.another.com',
'sub.sub.another.com',
'test.com',
'api.test.com',
'test.sh',
'api.test.sh',
'blah.api.test.sh']
Now that everything's in the correct order, do a similar grouping operation with itertools.groupby which groups by the blah.com part of x.y.z.blah.com:
def grouper(netloc):
return ''.join(netloc.split('.')[-2:])
#in-place sort, replicating sorted() call above
data.sort(key=sorter)
from itertools import groupby
[list(g) for k,g in groupby(data, grouper)]
Out[27]:
[['another.com', 'sub.another.com', 'sub.sub.another.com'],
['test.com', 'api.test.com'],
['test.sh', 'api.test.sh', 'blah.api.test.sh']]
Lastly, you need to format these groups into your desired hierarchy. Here is a quick-and-dirty implementation:
def make_hierarchy(groups):
from copy import deepcopy
_groups = deepcopy(groups)
ret = []
for li in _groups:
current = {}
ret.append(current)
while li:
current['name'] = li.pop()
if li:
nxt = {}
current['children'] = nxt
current = nxt
return ret
print(json.dumps(make_hierarchy(grouped), indent=2))
[
{
"children": {
"children": {
"name": "another.com"
},
"name": "sub.another.com"
},
"name": "sub.sub.another.com"
},
{
"children": {
"name": "test.com"
},
"name": "api.test.com"
},
{
"children": {
"children": {
"name": "test.sh"
},
"name": "api.test.sh"
},
"name": "blah.api.test.sh"
}
]
This last implementation depends on a couple of assumptions, namely that there will not be any equivalent-length netlocs in a given group, i.e. sub1.example.com and sub2.example.com will never happen. Obviously you can tweak the implementation as needed.
Related
So I have a flattened tree in JSON like this, as array of objects:
[{
aid: "id3",
data: ["id1", "id2"]
},
{
aid: "id1",
data: ["id3", "id2"]
},
{
aid: "id2",
nested_data: {aid: "id4", atype: "nested", data: ["id1", "id3"]},
data: []
}]
I want to gather that tree and resolve ids into data with recursion loops into something like this (say we start from "id3"):
{
"aid":"id3",
"payload":"1",
"data":[
{
"id1":{
"aid":"id1",
"data":[
{
"id3":null
},
{
"id2":null
}
]
}
},
{
"id2":{
"aid":"id2",
"nested_data":{
"aid":"id4",
"atype":"nested",
"data":[
{
"id1":null
},
{
"id3":null
}
]
},
"data":[
]
}
}
]
}
So that we would get breadth-first search and resolve some field into "value": "object with that field" on first entrance and "value": Null
How to do such a thing in python 3?
Apart from all the problems that your structure has in terms of syntax (identifiers must be within quotes, etc.), the code below will provide you with the requested answer.
But you should carefully think about what you are doing, and have the following into account:
Using the relations expressed in the flat structure that you provide will mean that you will have an endless recursion since you have items that include other items that in turn include the first ones (like id3 including id1, which in turn include id3. So, you have to define stop criteria, or be sure that this does not occur in your flat structure.
Your initial flat structure is better to be in the form of a dictionary, instead of a list of pairs {id, data}. That is why the first thing the code below does is to transform this.
Your final, desired structure contains a lot of redundancies in terms of information contained. Consider simplifying it.
Finally, you mentioned nothing about the "nested_data" nodes, and how they should be treated. I simply assumed that in case that exist, further expansion is required.
Please, consider trying to provide a bit of context in your questions, some real data examples (I believe the data provided is not real data, therefore the inconsistencies and redundancies), and try yourself and provide your efforts; that's the only way to learn.
from pprint import pprint
def reformat_flat_info(flat):
reformatted = {}
for o in flat:
key = o["aid"]
del o["aid"]
reformatted[key] = o
return reformatted
def expand_data(aid, flat, lvl=0):
obj = flat[aid]
if obj is None: return {aid: obj}
obj.update({"aid": aid})
if lvl > 1:
return {aid: None}
for nid,id in enumerate(obj["data"]):
obj["data"][nid] = expand_data(id, flat, lvl=lvl+1)
if "nested_data" in obj:
for nid,id in enumerate(obj["nested_data"]["data"]):
obj["nested_data"]["data"][nid] = expand_data(id, flat, lvl=lvl+1)
return {aid: obj}
# Provide the flat information structure
flat_info = [
{
"aid": "id3",
"data": ["id1", "id2"]
}, {
"aid": "id1",
"data": ["id3", "id2"]
}, {
"aid": "id2",
"nested_data": {"aid": "id4", "atype": "nested", "data": ["id1", "id3"]},
"data": []
}
]
pprint(flat_info)
print('-'*80)
# Reformat the flat information structure
new_flat_info = reformat_flat_info(flat=flat_info)
pprint(new_flat_info)
print('-'*80)
# Generate the result
starting_id = "id3"
result = expand_data(aid=starting_id, flat=new_flat_info)
pprint(result)
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore": "Warning",
"System.Net.Http.HttpClient.Default.ClientHandler": "Warning",
"System.Net.Http.HttpClient.Default.LogicalHandler": "Warning"
}
},
"AllowedHosts": "*",
"AutomaticTransferOptions": {
"DateOffsetForDirectoriesInDays": -1,
"DateOffsetForPortfoliosInDays": -3,
"Clause": {
"Item1": "1"
}
},
"Authentication": {
"ApiKeys": [
{
"Key": "AB8E5976-2A7C-4EEE-92C1-7B0B4DC840F6",
"OwnerName": "Cron job",
"Claims": [
{
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"Value": "StressTestManager"
}
]
},
{
"Key": "B11D4F27-483A-4234-8EC7-CA121712D5BE",
"OwnerName": "Test admin",
"Claims": [
{
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"Value": "StressTestAdmin"
},
{
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"Value": "TestManager"
}
]
},
{
"Key": "EBF98F2E-555E-4E66-9D77-5667E0AA1B54",
"OwnerName": "Test manager",
"Claims": [
{
"Type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
"Value": "TestManager"
}
]
}
],
"LDAP": {
"Domain": "domain.local",
"MachineAccountName": "Soft13",
"MachineAccountPassword": "vixuUEY7884*",
"EnableLdapClaimResolution": true
}
},
"Authorization": {
"Permissions": {
"Roles": [
{
"Role": "TestAdmin",
"Permissions": [
"transfers.create",
"bindings.create"
]
},
{
"Role": "TestManager",
"Permissions": [
"transfers.create"
]
}
]
}
}
}
I have JSON above and need to parse it with output like this
Logging__LogLevel__Default
Authentication__ApiKeys__0__Claims__0__Type
Everything is ok, but I always get some strings with this output
Authentication__ApiKeys__0__Key
Authentication__ApiKeys__0__OwnerName
Authentication__ApiKeys__0__Claims__0__Type
Authentication__ApiKeys__0__Claims__0__Value
Authentication__ApiKeys__0__Claims__0
Authentication__ApiKeys__2
Authorization__Permissions__Roles__0__Role
Authorization__Permissions__Roles__0__Permissions__1
Authorization__Permissions__Roles__1__Role
Authorization__Permissions__Roles__1__Permissions__0
Authorization__Permissions__Roles__1
Why does my code adds not full strings like
Authentication__ApiKeys__0__Claims__0
Authentication__ApiKeys__2
Authorization__Permissions__Roles__1
And why it doesn't print every value from
Authorization__Permissions__Roles__0__Permissions__*
and from
Authorization__Permissions__Roles__1__Permissions__*
I have this code in python3:
def checkdepth(sub_key, variable):
delmt = '__'
for item in sub_key:
try:
if isinstance(sub_key[item], dict):
sub_variable = variable + delmt + item
checkdepth(sub_key[item], sub_variable)
except TypeError:
continue
if isinstance(sub_key[item], list):
sub_variable = variable + delmt + item
for it in sub_key[item]:
sub_variable = variable + delmt + item + delmt + str(sub_key[item].index(it))
checkdepth(it, sub_variable)
print(sub_variable)
if isinstance(sub_key[item], int) or isinstance(sub_key[item], str):
sub_variable = variable + delmt + item
print (sub_variable)
for key in data:
if type(data[key]) is str:
print(key + '=' +str(data[key]))
else:
variable = key
checkdepth(data[key], variable)
I know that the problem in block where I process list data type, but I don't know where is the problem exactly
Use a recursive generator:
import json
with open('input.json') as f:
data = json.load(f)
def strkeys(data):
if isinstance(data,dict):
for k,v in data.items():
for item in strkeys(v):
yield f'{k}__{item}' if item else k
elif isinstance(data,list):
for i,v in enumerate(data):
for item in strkeys(v):
yield f'{i}__{item}' if item else str(i)
else:
yield None # termination condition, not a list or dict
for s in strkeys(data):
print(s)
Output:
Logging__LogLevel__Default
Logging__LogLevel__Microsoft
Logging__LogLevel__Microsoft.Hosting.Lifetime
Logging__LogLevel__Microsoft.AspNetCore
Logging__LogLevel__System.Net.Http.HttpClient.Default.ClientHandler
Logging__LogLevel__System.Net.Http.HttpClient.Default.LogicalHandler
AllowedHosts
AutomaticTransferOptions__DateOffsetForDirectoriesInDays
AutomaticTransferOptions__DateOffsetForPortfoliosInDays
AutomaticTransferOptions__Clause__Item1
Authentication__ApiKeys__0__Key
Authentication__ApiKeys__0__OwnerName
Authentication__ApiKeys__0__Claims__0__Type
Authentication__ApiKeys__0__Claims__0__Value
Authentication__ApiKeys__1__Key
Authentication__ApiKeys__1__OwnerName
Authentication__ApiKeys__1__Claims__0__Type
Authentication__ApiKeys__1__Claims__0__Value
Authentication__ApiKeys__1__Claims__1__Type
Authentication__ApiKeys__1__Claims__1__Value
Authentication__ApiKeys__2__Key
Authentication__ApiKeys__2__OwnerName
Authentication__ApiKeys__2__Claims__0__Type
Authentication__ApiKeys__2__Claims__0__Value
Authentication__LDAP__Domain
Authentication__LDAP__MachineAccountName
Authentication__LDAP__MachineAccountPassword
Authentication__LDAP__EnableLdapClaimResolution
Authorization__Permissions__Roles__0__Role
Authorization__Permissions__Roles__0__Permissions__0
Authorization__Permissions__Roles__0__Permissions__1
Authorization__Permissions__Roles__1__Role
Authorization__Permissions__Roles__1__Permissions__0
Using json_flatten this can be converted to pandas, but it's not clear if that's what you want. Also, when you do convert it can use df.iloc[0] to see why each column is being provided (ie you see the value for that key).
Note: you need to pass a list so I just wrapped your json above in [].
# https://github.com/amirziai/flatten
dic = your json from above
dic =[dic] # put it in a list
dic_flattened = (flatten(d, '__') for d in dic) # add your delimiter
df = pd.DataFrame(dic_flattened)
df.iloc[0]
Logging__LogLevel__Default Information
Logging__LogLevel__Microsoft Warning
Logging__LogLevel__Microsoft.Hosting.Lifetime Information
Logging__LogLevel__Microsoft.AspNetCore Warning
Logging__LogLevel__System.Net.Http.HttpClient.Default.ClientHandler Warning
Logging__LogLevel__System.Net.Http.HttpClient.Default.LogicalHandler Warning
AllowedHosts *
AutomaticTransferOptions__DateOffsetForDirectoriesInDays -1
AutomaticTransferOptions__DateOffsetForPortfoliosInDays -3
AutomaticTransferOptions__Clause__Item1 1
Authentication__ApiKeys__0__Key AB8E5976-2A7C-4EEE-92C1-7B0B4DC840F6
Authentication__ApiKeys__0__OwnerName Cron job
Authentication__ApiKeys__0__Claims__0__Type http://schemas.microsoft.com/ws/2008/06/identi...
Authentication__ApiKeys__0__Claims__0__Value StressTestManager
Authentication__ApiKeys__1__Key B11D4F27-483A-4234-8EC7-CA121712D5BE
Authentication__ApiKeys__1__OwnerName Test admin
Authentication__ApiKeys__1__Claims__0__Type http://schemas.microsoft.com/ws/2008/06/identi...
Authentication__ApiKeys__1__Claims__0__Value StressTestAdmin
Authentication__ApiKeys__1__Claims__1__Type http://schemas.microsoft.com/ws/2008/06/identi...
Authentication__ApiKeys__1__Claims__1__Value TestManager
Authentication__ApiKeys__2__Key EBF98F2E-555E-4E66-9D77-5667E0AA1B54
Authentication__ApiKeys__2__OwnerName Test manager
Authentication__ApiKeys__2__Claims__0__Type http://schemas.microsoft.com/ws/2008/06/identi...
Authentication__ApiKeys__2__Claims__0__Value TestManager
Authentication__LDAP__Domain domain.local
Authentication__LDAP__MachineAccountName Soft13
Authentication__LDAP__MachineAccountPassword vixuUEY7884*
Authentication__LDAP__EnableLdapClaimResolution true
Authorization__Permissions__Roles__0__Role TestAdmin
Authorization__Permissions__Roles__0__Permissions__0 transfers.create
Authorization__Permissions__Roles__0__Permissions__1 bindings.create
Authorization__Permissions__Roles__1__Role TestManager
Authorization__Permissions__Roles__1__Permissions__0 transfers.create
Ok, I looked at your code and it's hard to follow. You're variable and function names are not easy to understand their purpose. Which is fine cause everyone has to learn best practice and all the little tips and tricks in python. So hopefully I can help you out.
You have a recursive-ish function. Which is definingly the best way to handle a situation like this. However your code is part recursive and part not. If you go recursive to solve a problem you have to go 100% recursive.
Also the only time you should print in a recursive function is for debugging. Recursive functions should have an object that is passed down the function and gets appended to or altered and then passed back once it gets to the end of the recursion.
When you get a problem like this, think about which data you actually need or care about. In this problem we don't care about the values that are stored in the object, we just care about the keys. So we should write code that doesn't even bother looking at the value of something except to determine its type.
Here is some code I wrote up that should work for what you're wanting to do. But take note that because I did purely a recursive function my code base is small. Also my function uses a list that is passed around and added to and then at the end I return it so that we can use it for whatever we need. If you have questions just comment on this question and I'll answer the best I can.
def convert_to_delimited_keys(obj, parent_key='', delimiter='__', keys_list=None):
if keys_list is None: keys_list = []
if isinstance(obj, dict):
for k in obj:
convert_to_delimited_keys(obj[k], delimiter.join((parent_key, str(k))), delimiter, keys_list)
elif isinstance(obj, list):
for i, _ in enumerate(obj):
convert_to_delimited_keys(obj[i], delimiter.join((parent_key, str(i))), delimiter, keys_list)
else:
# Append to list, but remove the leading delimiter due to string.join
keys_list.append(parent_key[len(delimiter):])
return keys_list
for item in convert_to_delimited_keys(data):
print(item)
I have such slice of loaded json tp python dictionary (size_dict):
{
"sizeOptionName":"XS",
"sizeOptionId":"1528",
"sortOrderNumber":"7017"
},
{
"sizeOptionName":"S",
"sizeOptionId":"1529",
"sortOrderNumber":"7047"
},
{
"sizeOptionName":"M",
"sizeOptionId":"1530",
"sortOrderNumber":"7095"
}
and I have products with size Id (dictionary_prod):
{
"catalogItemId":"7627712",
"catalogItemTypeId":"3",
"regularPrice":"0.0",
"sizeDimension1Id":"1528",
"sizeDimension2Id":"0",
}
I need to make such as output for any product:
result_dict = {'variant':
[{"catalogItemId":"7627712", ...some other info...,
'sizeName': 'XS', 'sizeId': '1525'}}]}
so I need to convert size ID and add it to new result object
What is the best pythonic way to do this?
I dont know how to get right data from size_dict
if int(dictionary_prod['sizeDimension1Id']) > o:
(result_dict['variant']).append('sizeName': size_dict???)
As Tommy mentioned, this is best facilitated by mapping the size id's to their respective dictionaries.
size_dict = \
[
{
"sizeOptionName":"XS",
"sizeOptionId":"1528",
"sortOrderNumber":"7017"
},
{
"sizeOptionName":"S",
"sizeOptionId":"1529",
"sortOrderNumber":"7047"
},
{
"sizeOptionName":"M",
"sizeOptionId":"1530",
"sortOrderNumber":"7095"
}
]
size_id_map = {size["sizeOptionId"] : size for size in size_dict}
production_dict = \
[
{
"catalogItemId":"7627712",
"catalogItemTypeId":"3",
"regularPrice":"0.0",
"sizeDimension1Id":"1528",
"sizeDimension2Id":"0",
}
]
def make_variant(idict):
odict = idict.copy()
size_id = odict.pop("sizeDimension1Id")
odict.pop("sizeDimension2Id")
odict["sizeName"] = size_id_map[size_id]["sizeOptionName"]
odict["sizeId"] = size_id
return odict
result_dict = \
{
"variant" : [make_variant(product) for product in production_dict]
}
print(result_dict)
Your question is a little confusing but it looks like you have a list (size_dict) of dictionaries that contain some infroamtion and you want to do a lookup to find a particular element in the list that contains the SizeOptionName you are interested in so that you can read off the SizeOptionID.
So first you could organsie your size_dict as a dictionary rather than a list - i.e.
sizeDict = {"XS":{
"sizeOptionName":"XS",
"sizeOptionId":"1528",
"sortOrderNumber":"7017"
}, "S": {
"sizeOptionName":"S",
"sizeOptionId":"1529",
"sortOrderNumber":"7047"
}, ...
You could then read off the SizeOptionID you need by doing:
sizeDict[sizeNameYouAreLookingFor][SizeOptionID]
Alternative you could keep your current structure and just search the list of dictionaries that is size_dict.
So:
for elem in size_dict:
if elem.SizeOptionID == sizeYouAreLookingFor:
OptionID = elem.SizeOptionId
Or perhaps you are asking something else?
I want to take a list like the following:
groups = ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]
And convert it into a nested list, probably in the following format, but I'm open to suggestions:
groups_sorted = [{
"name":"foo",
"children": [
{
"name": "foo::fone",
"children": [ ... ]
}, ...
]
}, ...
]
So that the list is sorted using a hierarchy split on ::. I need each of the children keys to be lists themselves as the original order of the list is important.
I've played around for a few hours and been able to create a recursive dictionary starting from a single top node, but I couldn't do the last bit. Find my workings below:
def children_of(node, candidates):
children = []
remainder = []
for c in candidates:
sub = node + "::"
if c.startswith(sub):
try:
c[len(sub):].index("::") # any more separators = not a child
remainder.append(c)
except ValueError: # a child
children.append(c)
else: #not related
remainder.append(c)
return children, remainder
def sortit(l):
if l:
el = l.pop(0)
children, remainder = children_of(el,l)
if children:
return { "name": el,
"children": [sortit([c]+remainder) for c in children]
}
else:
return { "name": el }
Edit: #Thijs van Dien's solution is really good but I need 2.6 compatibility which prevents me some using OrderDicts.
How about something like this instead?
from collections import OrderedDict
dic = OrderedDict()
def insert(name):
current_dic = dic
current_name = ''
for name_elem in name.split('::'):
current_name += ('::' if current_name else '') + name_elem
if not current_name in current_dic:
current_dic[current_name] = OrderedDict()
current_dic = current_dic[current_name]
for group in ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]:
insert(group)
That gives you the following structure:
{'bar': {}, 'foo': {'foo::fone': {}, 'foo::ftwo': {'foo::ftwo::ffone': {}}}}
OrderedDict makes sure that order is preserved, so you don't need to use any list. Also, you don't need to use recursion, as it is not recommended in Python.
If you don't have OrderedDict in the standard library, because you're using Python 2.6, you can install it:
pip install ordereddict
Then change the import:
from ordereddict import OrderedDict
Here's another solution that works only if you can assume that parents already exist when you need them. Things go bad if you have duplicate groups, so you need to adjust it for that yourself.
children_of_name = dict([('', list())]) # Access root with empty string
def insert(name):
parent_name = '::'.join(name.split('::')[:-1])
dic = dict([('name', name), ('children', list())])
children_of_name[parent_name].append(dic)
children_of_name[name] = dic['children']
for group in ["foo", "bar", "foo::fone", "foo::ftwo", "foo::ftwo::ffone"]:
insert(group)
It gives you the structure that you proposed:
[{'children': [{'children': [], 'name': 'foo::fone'},
{'children': [{'children': [], 'name': 'foo::ftwo::ffone'}],
'name': 'foo::ftwo'}],
'name': 'foo'},
{'children': [], 'name': 'bar'}]
If I have the following python dict "mydict":
mydict = {
"folder": "myfolder",
"files": [
{ "folder": "subfolder",
"files": []
},
{ "folder": "anotherfolder",
"files": [
{ "folder": "subsubfolder",
"files": []
},
{ "folder": "subotherfolder",
"files": []
}
]
},
]
}
How can I make it such that if I have another dict "newdict":
newdict = {
"folder":"newfolder"
"files":[]
}
How/Is it possible to write a function the takes two arguments ("current dictionary to be added to","dict i want to add", "foldername-to-insert-under" such that after calling the function with:
desiredfunction(mydict, newdict, "subsubfolder")
I would like the function to search my existing "mydict" for the appropriate "files" array to append "newdict".
mydict = {
"folder": "myfolder",
"files": [
{ "folder": "subfolder",
"files": []
},
{ "folder": "anotherfolder",
"files": [
{ "folder": "subsubfolder",
"files": [
{
"folder":"newfolder"
"files":[]
}
]
},
{ "folder": "subotherfolder",
"files": []
}
]
},
]
}
What is the best way to do this? I do not know how/if its possible to search an existing dictionary structure, search within multiple levels of a nested dictionary to appropriately insert a new dict before, help is appreciated.
If you know the shape (amount of nested levels, types, etc.) of the data structure, you can just write a procedure containing the proper amount of for loops to iterate over the data structure and find the key.
If you don't know the shape of the data structure, you can recursively traverse the data structure until it's empty or the appropriate key has been found.
EDIT: Oh, you want to add a new dict inside the old dict. I thought you wanted to add something from the old dict to the new one.
ex. If you want to insert 'something' under the key 'z' in a dict, and you know what the dict is going to look like at all times,
example = {0: {'a': 'dummy'}, 1: {'z': 'dummy'}}
for thing1 in example:
for thing2 in thing1:
if thing2 == 'z':
example[thing1][thing2] = 'something'
If you don't know what the dict is going to look like, or if you don't want to hardcode the for loops,
example = {0: {'a': 'dummy'}, 1: {'b': {'z': 'dummy'}}}
replacement = 'something'
path = [1, 'b', 'z']
parent = example
while path:
key = path.pop(0)
if path:
parent = parent[key]
else:
parent[key] = replacement
print example
where path can be passed as an argument to a recursive call of a recursive procedure that traverses the dict.