find parent nodes in a tree - python

I created a JSON example and I would like to input an id and get that item and all parents.
Though my code does work, it doesn't feel pythonic, there must be a better way.
a_list = [
{
"name": {"en": "Canada"},
"id": "CAN",
"children": [
{
"name": {"en": "British Columbia"},
"id": "01",
"children": [
{"name": {"en": "Vancouver"}, "id": "10", "children": []},
{"name": {"en": "Victoria"}, "id": "11", "children": []}
]
},
{
"name": {"en": "Nova Scotia"},
"id": "02",
"children": [
{"name": {"en": "Halifax"}, "id": "13", "children": []}
]
}
],
"name": {"en": "USA"},
"id": "US",
"children": [
{
"name": {"en": "Virgina"},
"id": "VA",
"children": [
{"name": {"en": "Richmond"}, "id": "20", "children": []},
{"name": {"en": "Norfolk"}, "id": "21", "children": []}
]
},
{
"name": {"en": "Maryland"},
"id": "MD",
"children": [
{"name": {"en": "Baltimore"}, "id": "23", "children": []},
{"name": {"en": "Gaithersburg"}, "id": "24", "children": []}
]
}
]
}
]
so I created this function
def find_item_and_parents_in_list(value, items):
item_found = []
for item in items:
if item['id'] == value:
item_found.append(item)
else:
item_list = find_item_and_parents_in_list(value, item['children'])
if item_list:
item_list.append(item)
item_found = item_list
return item_found
if I use it, i.e
items = find_item_in_list("23", a_list)
for item in items:
print(item['id'])
it returns
23
MD
US
but can my find_item_and_parents_in_list be written in a better way? using yield for example

You can use recursion with a generator:
def get_paths(d, _id, c = []):
if d['id'] == _id:
yield c+[_id]
yield from [i for b in d['children'] for i in get_paths(b, _id, c+[d['id']])]
print(list(get_paths(a_list[0], '23'))[0])
Output:
['US', 'MD', '23']

Related

How to compare two list of dictionary and update value from another [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
list_1 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Owner" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "100", "name": "last", "email": "last#network.com", "type": "Manager" }
]
list_2 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "52", "name": "abcded", "email": "abcded#network.com", "type": "Manager" }
]
A preference dictionary {'Owner': 1, 'Manager':2, 'employ':3, 'HR': 4 }
There are two list of dictionaries
list_1 is the primary_dictionary, list_2 is the secondary_dictionary
I need to update the role in list_1 dictionary if the 'email' present in secondary dictionary with respect to check in preference dictionary
In the end output i should contain 'type' as in preference dictionary
If any emails match I want to update list one at all places with that email to contain a new role under'type'. for example if 123#gmail.com was in a dictionary within list2 then I want to change every item in list1 that contains 123#gmail.com as the email to have a role that is determined by a selection from the preference dictionary.
Expected out
[
{'id': '11', 'name': 'son', 'email': 'n#network.com', 'type': 'Owner'},
{'id': '21', 'name': 'abc', 'email': 'abc#network.com', 'type': 'Manager'},
{"id": "100", "name": "last", "email": "last#network.com", "type": "Manager"}
]
Code is below
for each1 in list_1:
for each2 in list_2:
if each1['email'] == each2['email']:
# Update the list_1 dictionary with respect to preference
Given the following inputs:
list_1 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Owner" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "100", "name": "last", "email": "last#network.com", "type": "Manager" }
]
list_2 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "52", "name": "abcded", "email": "abcded#network.com", "type": "Manager" }
]
preference = {'Owner': 1, 'Manager':2, 'Employ':3, 'HR': 4 }
I would start by reshaping list_2 into something more useable. This will get rid of the duplicates leaving us just the "best" type for each email:
list_2_lookup = {}
for item in list_2:
key, value = item["email"], item["type"]
list_2_lookup.setdefault(key, value)
if preference[value] < preference[list_2_lookup[key]]:
list_2_lookup[key] = value
Then we can iterate over the items in the first list and use the lookup we just created. Note, this is a little more convoluted than might be needed as it is not clear from your question and your expected result what items from list_1 should actually appear in the output. I have tried to match your stated output.
result = {}
for item in list_1:
key = item["email"]
result.setdefault(key, item)
if preference[result[key]["type"]] > preference[item["type"]]:
result[key]["type"] = item["type"]
if preference[result[key]["type"]] > preference.get(list_2_lookup.get(key), 99):
result[key]["type"] = list_2_lookup.get(key)
At this point we can:
print(list(result.values()))
Giving us:
[
{'id': '11', 'name': 'son', 'email': 'n#network.com', 'type': 'Owner'},
{'id': '21', 'name': 'abc', 'email': 'abc#network.com', 'type': 'Manager'},
{'id': '100', 'name': 'last', 'email': 'last#network.com', 'type': 'Manager'}
]
Here is code that will do the following:
If any emails match update list one at all places with that email to contain a new role under 'type'. for example if 123#gmail.com was in a dictionary within list2 then it will change change every item in list1 that contains 123#gmail.com as the email to have a role that is determined by a selection from the preference dictionary.
list_1 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Owner" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "100", "name": "last", "email": "last#network.com", "type": "Manager" }
]
list_2 = [
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "11", "name": "son", "email": "n#network.com", "type": "Manager" },
{ "id": "21", "name": "abc", "email": "abc#network.com", "type": "Employ" },
{ "id": "52", "name": "abcded", "email": "abcded#network.com", "type": "Manager" }
]
pref = {1: 'owner', 2: 'Manager', 3: 'employ', 4: 'HR' }
choice = 1
idx1 = -1
for each1 in list_1:
idx1 += 1
for each2 in list_2:
if each1['email'] == each2['email']:
print(list_1[idx1]['type'])
print(pref[choice])
list_1[idx1]['type'] = pref[choice]
print(list_1)

How to create a priority list of dictionary

list is below
preference dictionary is below
if all the keys and values except type will be same then ..
Need to compare type in each list which is highest order in preference dictionary
Output is list of dictionary which type is highest order
list_ = [
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Owner"
},
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Manager"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Employ"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Manager"
}
]
A preference dictionary = {'Owner': 1, 'Manager':2, 'employ':3, 'HR': 4 }
My expected output dictionary below
[{'id': '11', 'name': 'son', 'email': 'n#network.com', 'type': 'Owner'},
{'id':'21','name': 'abc','email': 'abc#network.com','type': 'Manager'}]
new_list = []
for each in list_:
if each['type'] in priority.keys():
if each['id'] not in new_list:
new_list.append(each)
You can simply do src.sort(key = lambda x : preference[x["type"]]) and your list will be sorted.
This solution groups all the elements by id, and sorts the groups according to the preference (so that the Owner is first and HR is last) and then just picks the first from each group:
from collections import defaultdict
src = [
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Owner"
},
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Manager"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Employ"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Manager"
}
]
preference = {'Owner': 1, 'Manager':2, 'Employ':3, 'HR': 4 }
d = defaultdict(list)
# group all the records by id
for item in src:
d[item['id']].append(item)
# sort each group by the preference
for item in d.values():
item.sort(key=lambda x: preference[x['type']])
# select only the first from each group
result = [item[0] for item in d.values()]
print(result)
Output:
[{'id': '11', 'name': 'son', 'email': 'n#network.com', 'type': 'Owner'}, {'id': '21', 'name': 'abc', 'email': 'abc#network.com', 'type': 'Manager'}]
You could create a priority queue:
from queue import PriorityQueue
priority = {'Owner': 1, 'Manager':2, 'employ':3, 'HR': 4 }
q = PriorityQueue()
for elem in list_:
p = priority[elem['type']]
q.put((p, id(elem), elem))
Or you could also sort a list based on the type with:
priority_list = sorted(list_, key=lambda x: priority[x['type']], reverse=True)
Well here's my shot!
It isn't beautiful but it seems to work.
list_ = [
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Owner"
},
{
"id": "11",
"name": "son",
"email": "n#network.com",
"type": "Manager"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Employ"
},
{
"id": "21",
"name": "abc",
"email": "abc#network.com",
"type": "Manager"
}
]
new = dict( Owner = 0, Manager = 0, Employ = 0, HR = 0 )
for a in list_ :
type_ = a[ 'type' ]
if type_ == 'Owner':
new[ 'Owner' ] += 1
if type_ == 'Manager':
new[ 'Manager' ] += 1
if type_ in [ 'Employ', 'Manager' ]:
new[ 'Employ' ] += 1
new[ 'HR' ] += 1
print( new )

Traversing through a tree (multi nest)

There is a parent, children, grandchildren and many more relationship like the json given below (id's are unique inside each list), if the nesting is up to 10 levels with each level having different lengths. What is the best way to find a uni_code to insert another object (child) into its list?
{
"id": "1",
"status": "active",
"created": "",
"children": [{
"id": "1",
"status": "active",
"created": "",
"children": [{
"id": "1",
"status": "active",
"created": "",
"children": [{
"id": "1",
"status": "active",
"created": "",
"children": [
],
"uni_code": "EGCFJ1"
},
{
"id": "1",
"status": "active",
"created": "",
"children": [
],
"uni_code": "D356RY"
},
],
"uni_code": "EGCFJ1"
},
],
"uni_code": "Y7TUP8"
},
{
"id": "4",
"status": "active",
"created": "",
"children": [
],
"uni_code": "WA1JNS"
},
],
"uni_code": "I429TD"
}
Based on your follow-on question in a comment, I believe I now understand what your were asking…and have updated my answer accordingly.
Often you can traverse a recursive data-structure like a tree by using a function that calls itself recursively.
What I mean:
def insert_into_tree(tree, new_child, target):
if tree['uni_code'] == target:
tree['children'].append(new_child)
return True
for child in tree['children']:
if insert_into_tree(child, new_child, target):
return True
return False # `target` not found.
data = {
"id": "1",
"status": "active",
"created": "",
"children": [
{
"id": "1",
"status": "active",
"created": "",
"children": [
{
"id": "1",
"status": "active",
"created": "",
"children": [
{
"id": "1",
"status": "active",
"created": "",
"children": [],
"uni_code": "EGCFJ1"
},
{
"id": "1",
"status": "active",
"created": "",
"children": [],
"uni_code": "D356RY"
}
],
"uni_code": "EGCFJ1"
}
],
"uni_code": "Y7TUP8"
},
{
"id": "4",
"status": "active",
"created": "",
"children": [],
"uni_code": "WA1JNS"
}
],
"uni_code": "I429TD"
}
new_object = {
"id": "1",
"status": "active",
"created": "",
"children": [],
"uni_code": "THX1138"
}
target = "EGCFJ1"
if insert_into_tree(data, new_object, target):
print(f'new child added to {target!r}')
else:
print(f'{target!r} not found, new child not added')

Writing dictionaries recursively

I have a JSON file with location data. Below is a sample from the file.
[
{
"id": 1,
"name": "Western Cape",
"filename": "1",
"type": "Province",
"typeCode": 1
},
{
"id": 2,
"name": "Eastern Cape",
"filename": "2",
"type": "Province",
"typeCode": 1
},
{
"id": 3,
"name": "Northern Cape",
"filename": "3",
"type": "Province",
"typeCode": 1
},
{
"id": 4,
"name": "Free State",
"filename": "4",
"type": "Province",
"typeCode": 1
},
{
"id": 5,
"name": "KwaZulu-Natal",
"filename": "5",
"type": "Province",
"typeCode": 1
},
{
"id": 6,
"name": "North West",
"filename": "6",
"type": "Province",
"typeCode": 1
},
{
"id": 7,
"name": "Gauteng",
"filename": "7",
"type": "Province",
"typeCode": 1
},
{
"id": 8,
"name": "Mpumalanga",
"filename": "8",
"type": "Province",
"typeCode": 1
},
{
"id": 9,
"name": "Limpopo",
"filename": "9",
"type": "Province",
"typeCode": 1
},
{
"id": 199,
"name": "City of Cape Town",
"filename": "1.199",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 1
},
{
"id": 260,
"name": "Buffalo City",
"filename": "2.260",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 2
},
{
"id": 299,
"name": "Nelson Mandela Bay",
"filename": "2.299",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 2
},
{
"id": 499,
"name": "Mangaung",
"filename": "4.499",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 4
},
{
"id": 599,
"name": "eThekwini",
"filename": "5.599",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 5
},
{
"id": 797,
"name": "Ekurhuleni",
"filename": "7.797",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 7
},
{
"id": 798,
"name": "City of Johannesburg",
"filename": "7.798",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 7
},
{
"id": 799,
"name": "City of Tshwane",
"filename": "7.799",
"type": "Metropolitan Municipality",
"typeCode": 2,
"parent": 7
}]
I am looking to achieve the following output:
{'Eastern Cape': {u'Buffalo City': {}, u'Nelson Mandela Bay': {}}, 'Gauteng': {u'Ekurhuleni': {}, u'City of Johannesburg': {}, u'City of Tshwane': {}}, 'North West': {}, 'Mpumalanga': {}, 'Limpopo': {}, 'Western Cape': {u'City of Cape Town': {}}, 'KwaZulu-Natal': {u'eThekwini': {}}, 'Northern Cape': {}, 'Free State': {u'Mangaung': {}}}
I have written the following code block to achieve it:
province_dict = {}
final_dict = {
'Western Cape': {},
'Eastern Cape': {},
'Northern Cape': {},
'Free State': {},
'KwaZulu-Natal': {},
'North West': {},
'Gauteng': {},
'Mpumalanga': {},
'Limpopo': {},
}
for item in data:
if item['type'] == 'Province':
province_dict.update({item['id']: item['name']})
for item in data:
if item['type'] != 'Province':
if item['parent'] in province_dict.keys():
final_dict[province_dict[item['parent']]].update({item['name']: {}})
print final_dict
However, there seems to some problems:
This isn't the most pythonic way to achieve this.
I am not limited to Province and Metro Municipality, I also have District Municipalities and so on. They are however all governed by the same rules every child has a parent id with the Province being the root.
I need to create a hierarchical structure as mentioned above with n number of nesting possible.
It would be helpful if someone could help me achieve this.
I think you might want to create an abstract data type to do that efficiently. You could have the ADT class use a dictionary and you could give it another property.
This is just pseudocoding. I don't have time at the moment to create a fully functional class, but I think this would help.
class SomeClass:
_parents = {}
_children = {}
def add_obj(self, obj):
if obj.noParent:
self._parents[obj.get_id] = obj
else:
self._children[obj.get_id] = obj
def to_string(self, id):
for v1 in (self._parents if id is None else self._parents.get(id)):
if v1.get_id in self._parents.keys():
print(self.to_string(v1.get_id()))
for v2 in self._children.get(id):
print(v2.stuff)
If you are still struggling, when I have time either later today or tomorrow, I'll come back to this answer and see if I can make it functional.

Flatten nested JSON arrays with inherits properties in Python

I have a big json/dictionary with different levels of nested json arrays, I would like to flatten it, and also capture the relationship of the structure,
Part of my json looks like:
{
"name": "root",
"type": "all",
"children": [
{
"name": "properties",
"type": "feature",
"children": [
{
"name": "print",
"type": "feature",
"children": [
{
"name": "graphic print",
"type": "feature",
"inherits": true
},
{
"name": "striped print",
"type": "feature",
"inherits": true,
"children": [
{
"name": "pinstriped",
"type": "feature",
"inherits": true
},
{
"name": "light stripe",
"type": "feature",
"inherits": true
},
{
"name": "wide stripe",
"type": "feature",
"inherits": true
}
]
}
]
}
]
},
{
"name": "colours",
"type": "colour",
"children": [
{
"name": "main colours",
"type": "colour",
"children": [
{
"name": "black",
"type": "colour",
"children": [
{
"name": "light black",
"type": "colour",
"inherits": true
},
{
"name": "blue black",
"type": "colour",
"inherits": true
}
]
},
{
"name": "red",
"type": "colour",
"children": [
{
"name": "bright red",
"type": "colour",
"inherits": true
},
{
"name": "light red",
"type": "colour"
}
]
}
]
}
]
},
{
"name": "genders",
"type": "gender",
"children": [
{
"name": "female",
"type": "gender"
},
{
"name": "male",
"type": "gender"
}
]
}
]
}
The depth of nests is not all the same. I
- want all the nodes (values of "name")
- also want all its parents if the node has "Inherit" key of True value.
Something like:
But if there are better ideas on how to store this data, will be happy to accept as well!
Many Thanks!
I think this should do your need
def parse_dict_of_dict(_dict, _parent = '', ret_dict={}):
_name, _children, _inherit = _dict["name"], _dict.get('children', None), _dict.get('inherits', False)
if _children is not None:
if isinstance(_children, list):
for _child in _children:
parse_dict_of_dict(_child, _name+ ', ' + _parent if _inherit else _name , ret_dict)
ret_dict[ _name] = _parent.strip(' ').strip(',') if _inherit else None
return ret_dict
Can you elaborate more on your output?
OR you can use this function to flatten a nested JSON to a simple JSON.
def parse_dict_of_dict(_dict, _str = ''):
ret_dict = {}
for k, v in _dict.iteritems():
if isinstance(v, dict):
ret_dict.update(parse_dict_of_dict(v, _str= _str+k+'_'))
elif isinstance(v, list):
for index, item in enumerate(v):
if isinstance(item, dict):
ret_dict.update(parse_dict_of_dict(item, _str=_str+k+'_%d_'%(index)))
else:
ret_dict.update({k+'_%d'%(index): item})
else:
try:
ret_dict[_str + k] = str(v)
except Exception as e:
ret_dict[_str + k] = unicode.encode(v, errors='ignore')
return ret_dict

Categories

Resources