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 1 year ago.
Improve this question
body = {
'a': 1,
'b': 'apple',
'child': [
{
'a': 12,
'b': 'banana',
'child': [
{
'a': 121,
'b': 'mango',
'child': [
{
'a': 1211,
'b': 'coconut',
'child': [dynamic nested]
}
]
},
{
'a': 122,
'b': 'papaya',
'child': [
{
'a': 1221,
'b': 'lemon',
'child': [dynamic nested]
}
]
}
]
},
{
'a': 13,
'b': 'orenge',
'child': [
dynamic nested
]
}
]
}
if i want know index of 'coconut' or 'lemon' (json body dynamic children sub child i don't know dee child, but khow 'a' or 'b' for find index deep)
how to get index with python?
ex1: index of 'coconut' = [0,0,0]
ex2: index of 'lemon' = [0,1,0]
def find(child, value, index=False):
# First element in nested object is searched value
if child["b"] == value:
return []
if len(child["child"]) == 0:
return False
for i in range(len(child["child"])):
if child["child"][i]["b"] == value:
index = [i]
break
else:
index = find(child["child"][i], value, index)
if index != False:
index = [i] + index
break
return index
print(find(body, "coconut"))
Use a recursive function
For educational purposes, the same Akane's algorithm but in a little bit more Pythonic style:
def find_fruit(child, value, index=False):
if child["b"] == value:
return []
for i, ch in enumerate(child["child"]):
if ch["b"] == value:
return [i]
index = find_fruit(ch, value, index)
if index:
return [i] + index
print(find(body, "coconut"))
Related
As json example, I have this:
{
"id": "foo",
"items": [
{
"id": "aaa",
"colour": "blue"
},
{
"id": "bbb",
"colour": "red",
}
]
}
Once json is read in Python into a dictionary:
What I want to do is a function that receives field to fill and value to be set
For example:
func(json_dict, 'items[0].colour', 'green')
or
func(json_dict, 'id', 'bar')
And the challenge is to do this for whatever json with whatever deep I have.
But I don't know how dynamically I can do:
json_dict['items'][0]['colour'] = 'green'
json_dict['id'] = 'bar'
Parse the path and define an array of keys to traverse. Then recursively find the last key and set the value.
import re
def set_val(json_obj, path, val):
path_arr = []
for _, k, i in re.findall(r'((\w+)(\[\d+\])?\.?)', path):
path_arr.append(k)
if i: path_arr.append(int(i[1:-1]))
def _get(c_path, curr=json_obj):
if len(c_path) == 1: curr[c_path[0]] = val
else: _get(c_path[1:], curr[c_path[0]])
_get(path_arr)
my_json = {'a': {'b': [1, 2, {'x': 2}]}}
set_val(my_json, 'a.b[2].x', 5)
print(my_json) # ==> {'a': {'b': [1, 2, {'x': 5}]}}
Here's one alternative to a regex approach. However, a minor disadvantage is you can't use a 'a.b[2].x' syntax anymore.
from typing import Tuple, Union
def set_val(d, path, val):
*paths, last_path = _get_dot_path(path)
path_dict = d
try:
for i, p in enumerate(paths):
path_dict = path_dict[p]
path_dict[last_path] = val
except (KeyError, IndexError, TypeError):
# TODO use variables i and p, if needed
raise
def _get_dot_path(path: str) -> Tuple[Union[str, int], ...]:
return tuple([int(s) if s.lstrip('-').isdecimal() else s
for s in path.split('.')])
my_json = {'a': {'b': [1, 2, {'x': 2}]}}
set_val(my_json, 'a.b.2.x', 5)
print(my_json) # {'a': {'b': [1, 2, {'x': 5}]}}
Assuming that I have a nested dictionary, that is extracted from pickle file, that contains various levels, I would like to get the value by giving only the last key. Keys are unique considering own 'branch'.
The main problem is that I have multiple keys and levels:
dict = {
'A': {
'X': {
1: [...],
2: [...]
},
'Y': {
3: [...],
4: [...]
}
}
'B': {
'G': {
'H': {
'Z': [...]
}
}
}
'C': [...]
}
How can I do that?
a simple solution would be a recusrive function that even works for nested, nested dictionarys
outer_dict = {'outer': {'inner': 10, 'even_inner': {'innerst': 25}}}
and the function:
def get_val(search_dict, key):
""" recursive searching the dict """
for elem in search_dict:
if elem == key:
return search_dict[elem]
if isinstance(search_dict[elem], dict):
retval = get_val(search_dict[elem], key)
if retval is not None:
return retval
value = get_val(outer_dict, 'innerst')
print(value)
>> 25
Problems:
if the key is not unique you will get only the first match. You will need a list to fill a values into if the key can be there more than once.
Please provide a example next time!
I've two dictionaries one original & the other reference. And I want to match reference dict keys with original dict keys and extract all those keys, values from original which are present in reference.
For example
original_dict = {
'a': {
'1': [1, 2, 3, 4, 5]
},
'b': {
'1': {
'A': [1, 2, 3, 4]
}
},
'c': {
'3': [1]
}
}
And a reference dictionary
reference_dict = {
'a': {
'2': [1, 2, 3]
},
'b': {
'1': {
'A': []
}
},
'c': {
'3': []
}
}
And this is the extracted dictionary.
extracted_dict = {
'b': {
'1': {
'A': [1, 2, 3, 4]
}
},
'c': {
'3': [1]
}
}
Here you might have noticed that I don't care about values of reference dict. All I want is the values of original dict.
You can use recursion to accomplish this:
def merge(value_dict, key_dict):
ret = {}
for key, subdict in key_dict.items():
if key in value_dict:
if not isinstance(value_dict[key], dict):
# we found a value so don't keep recursing
ret[key] = value_dict[key]
else:
# found another dict so merge subdicts
merged = merge(value_dict[key], subdict)
if len(merged) > 0:
ret[key] = merged
return ret
merge(original_dict, reference_dict)
I´m not sure if I understand your question 100% but this might be a solution.
we use relevant keys and put them in a list to use them on a .update() dict
new_lst = []
for i in original_dict.keys():
new_lst.append(original_dict[i])
reference_dict.clear()
for i in new_lst:
reference_dict.update(i)
print(reference_dict)
I am new to Python and I have not been able to find a good answer for my problem after looking for a while.
I am trying to create a Pandas dataframe from a list of dictionaries.
My list of nested dictionaries is the following:
{'category_1': [{'a': '151',
'b': '116',
'c': '86'}],
'category_2': [{'d': '201',
'e': '211',
'f': '252'},
{'d': '-1',
'e': '-9',
'f': '-7'}],
'category_3': {'g': 'Valid',
'h': None,
'i': False,
'j': False},
'category_4': {'k': None,
'l': None,
'm': None,
'n': None}}
And my output should be
a b c d e f g h i j k l m n
0 151 116 86 201,-1 211,-9 252,-7 valid None False False None None None None
What i tried,
I'm able to do category 1,3,4 but couldn't figure out the 2nd category
I tried concat and for nested loop to get it
ex=pd.concat([pd.Series(d) for d in (eg1)], axis=1).T
Then mergiting it.
As i said, couldn't figure out in the whole!
I wrote a short recursive function that returns a series, or a concatenation of several series if one of the keys in your dict (e.g category_2) contains a list of multiple dicts.
def expand(x):
if type(x) == dict:
return pd.Series(x)
elif type(x) == list:
return pd.concat([expand(i) for i in x])
If I start with the dictionary that you pasted in in your example above:
d = {'category_1': [{'a': '151',
'b': '116',
'c': '86'}],
'category_2': [{'d': '201',
'e': '211',
'f': '252'},
{'d': '-1',
'e': '-9',
'f': '-7'}],
'category_3': {'g': 'Valid',
'h': None,
'i': False,
'j': False},
'category_4': {'k': None,
'l': None,
'm': None,
'n': None}}
Then it's just a matter of concatenating all the series created by the recursive method I wrote:
output = pd.concat([expand(value) for key, value in d.items()])
And merging any duplicate indices so that their items appear in one row and are separated by commas. I also reshape the series into a df with one row and several columns:
output = pd.DataFrame(output.groupby(output.index).apply(lambda x: ','.join(x.astype(str)))).T
This results in a dataframe that matches your desired output:
output
a b c d e f g h i j k l m n
0 151 116 86 201,-1 211,-9 252,-7 Valid None Invalid Invalid None None None None
The code below recursively tries to flatten the input structure that can have lists or other dicts. When it hit the leafs, adds the content to a flattened dict and then convert it to a dataframe.
flattened_dict = {}
def flatten(obj, name = ''):
if isinstance(obj, dict):
for key, value in obj.items():
flatten(obj[key], key)
elif isinstance(obj, list):
for e in obj:
flatten(e)
else:
if obj == 'null':
obj = None
flattened_dict[name] = [obj]
flatten(eg1)
The result is:
Please note that you have to define the null as a string. The definition for the original dict is:
eg1 = {
"my_list": {
"category_1": [
{
"a": "151",
"b": "116",
"c": "86"
}
],
"category_2": [
{
"d": "201",
"e": "211",
"f": "252"
},
{
"d": "-1 ",
"e": "-9",
"f": "-7"
}
],
"category_3": {
"g": "Valid",
"h": "null",
"i": "Invalid",
"j": "Invalid"
},
"category_4": {
"k": "null",
"l": "null",
"m": "null",
"n": "null"
}
}
}
If I have a deeply nested dict is there a built-in way to subtract/remove list of "paths" (eg: keyA.keyB.key1, keyA.keyC.key2, etc) or a the keys of a second dict from the original dict? Or maybe there is a common module which has functionality like this?
Here's a suggestion:
D = { "keyA": {
"keyB" : {
"keyC" : 42,
"keyD": 13
},
"keyE" : 55
}
}
def remove_path(dictionary, path):
for node in path[:-1]:
dictionary = dictionary[node]
del dictionary[path[-1]]
remove_path(D, ["keyA", "keyB", "keyD"])
print D # prints {'keyA': {'keyB': {'keyC': 42}, 'keyE': 55}}
You'll probably want to introduce some error checking, too.
Just in case the other answers aren't what you're looking for, here's one that subtracts one dictionary from another.
def subtract(a, b):
""" Remove the keys in b from a. """
for k in b:
if k in a:
if isinstance(b[k], dict):
subtract(a[k], b[k])
else:
del a[k]
Another solution:
d = {
'A' : {
'C' : {
'D' : {
'E' : 4,
},
'F' : 5,
},
},
'B' : 2,
}
def DeepDictDel(path, dict):
for key in path.split('.'):
owner = dict
dict = dict[key]
del owner[key]
print d # prints {'A': {'C': {'D': {'E': 4}, 'F': 5}}, 'B': 2}
DeepDictDel('A.C.D', d)
print d # prints {'A': {'C': {'F': 5}}, 'B': 2}