Navigating JSON with variable keys in Python? [duplicate] - python

This question already has answers here:
Use Variable As Dictionary Key Set
(2 answers)
How to use a dot "." to access members of dictionary?
(36 answers)
Closed last month.
Lets say I have some json like so store in a variable called data
{
"print": {
"ams": { "exists": 1},
"fan_speed": 29,
"reports": [
{"name": "foo"},
{"name": "bar"}
]
}
}
Now I've got a variable which is the key i want to return stored in a variable called key for example print.fan_speed, print.ams.exists, print.reports[0].name
What I want to is something like data.get(key). What is the best way to approach this?

The following should work its way into your data, including indexing into lists:
import re
data = {
"print": {
"ams": { "exists": 1},
"fan_speed": 29,
"reports": [
{"name": "foo"},
{"name": "bar"}
]
}
}
def value_of(data, location):
for part in location.split("."):
match = re.match(r"(.*)\[(\d+)\]$", part)
if match:
name, index = match.groups()
data = data.get(name)[int(index)]
else:
data = data.get(part)
if not data:
return None
return data
print(value_of(data, "print.ams.exists"))
print(value_of(data, "print.reports[1].name"))
Result:
1
bar
It could do with a little rationalisation as it will return None for a non-existent key, but will error on a bad index - it should do one or the other depending on your requirements but the concept is there.
The concept is to take each '.' separated element of the string in turn, using it to dig further into the data structure. If the element matches the syntax of 'name[index]' using the regex, the component is treated as a list and the indexth element is extracted.

Related

Python search and replace whilst caching [duplicate]

This question already has answers here:
How can I make a dictionary (dict) from separate lists of keys and values?
(21 answers)
Closed 4 months ago.
I'm attempting to search and replace using information from 2 lists, this is whilst caching any replacements that have been done so the same corresponding values can be given.
For example, I have the following -
names = ["Mark","Steve","Mark","Chrome","192.168.0.1","Mark","Chrome","192.168.0.1","192.168.0.2"]
type = ["user","user","user","process","address","user","process","adress","address"]
And I'm hoping to get the following output -
{
"Mark":"user1",
"Steve":"user2",
"Chrome":"process1",
"192.168.0.1":"adress1",
"192.168.0.2":"adress2"
}
So trying to use the type in the the 2nd list to determine the item in the first list's corresponding value.
Hope this makes sense, is this possible? Any help would be appreciated.
I would recommend you use a dictionary personally.
names = {
"Mark": "user",
"Steve": "user2",
"Chrome": "process1",
"192.168.0.1": "address1",
"192.168.0.2": "address2"
}
print(names["Mark"])
By using this dictionary you can precisely tap into the name you'd like to information of or anything else you want. It is also a little more readable
To form a dictionary from said values you can iterate the range and access values with the same index:
output = {names[i]: types[i] for i in range(len(names))}
Also refrain from using variable name type because it's already taken by a builtin Python syntax.
Looks like you're also trying to store / retrieve the count of the types (i.e. "user1", "user2, "address1", etc.). Hence, we need another data structure to keep count of the types already registered in our "hashmap" (dictionary in python). In the below solution, we use the type_cache.
The code should work as is.
from collections import defaultdict
names = ["Mark", "Steve", "Mark", "Chrome", "192.168.0.1", "Mark", "Chrome", "192.168.0.1", "192.168.0.2"]
types = ["user", "user", "user", "process", "address", "user", "process", "address", "address"]
expected = {
"Mark": "user1",
"Steve": "user2",
"Chrome": "process1",
"192.168.0.1": "address1",
"192.168.0.2": "address2"
}
def process_names_and_types(names, types):
result = {}
type_cache = defaultdict(int)
for name, type_ in zip(names, types):
if name not in result:
type_cache[type_] += 1
result[name] = f"{type_}{type_cache[type_]}"
return result
if __name__ == "__main__":
actual = process_names_and_types(names, types)
assert actual == expected, f"Expected: {expected}, Actual: {actual}"

Looking up a value in a nested dictionary to get a value in a separate dictionary but same parent dictionary

In the response that comes back from an API request. I get a format that is a list of nested dictionaries. for example.
{
"data": [
{
"3": {
"value": 1
},
"6": {
"value": "Conversion"
},
"7": {
"value": "HVAC"
}
},
I can easily get past the the first dictionary using r['data']. At this point, each list item is a record in a database. r['data'][0] gives me a dictionary of what the field ('3') is and then a dictionary of what the value is ({'value': 'Conversion'}).
I want to be able to look up a value like 'Conversion' and have it tell me what the value is for field '3'. Anyway to do this using python?
Your description doesn't quite fit with reality. Let's have a complete structure:
r = {
"data": [
{
"3": {
"value": 1
},
"6": {
"value": "Conversion"
},
"7": {
"value": "HVAC"
}
}
]
}
r is a dictionary. It contains a single key ('data') whose associated value is a list which, in this example, contains one dictionary. That dictionary has 3 keys - "3", "6" and "7". Each of those keys has a value which itself is a dictionary comprised of a single key ('value') and, obviously, an associated value.
You can assert as follows:
assert r['data'][0]['6']['value'] == 'Conversion'
Hopefully that shows how you can access the lower level value(s)
What's unclear from your question is why would you be searching for 'Conversion' when you want the value from key '3' which would be:
r['data'][0]['3']['value']
EDIT:
def get_value(r, from_key, ref_key, value):
for d in r['data']:
if d.get(from_key, {}).get('value') == value:
return d.get(ref_key, {}).get('value')
print(get_value(r, '6', '3', 'Conversion'))
Doing it this way offers the flexibility of specifying the relevant keys and value
I have to make an assumption that what you are trying to say is that you want the index based on the value.
r = {...} # your data
def lookup(val, r_data):
for i in range(len(r_data['data'])): #loops through list based on index
for k, v in r_data['data'][i].items(): #loops dict with key/value
if v['value'] == val: #checks if value matches needle
return "[{}][{}]".format(i, k)
This returns '[0][6]'. I don't know if your data is supposed to be formatted that way... a list within 'data' and then only one dict inside. But, given this format, this will give you the index.

Create a single dictionary of nested dictionaries from *multiple* dot notation strings [duplicate]

This question already has answers here:
Setting a value in a nested Python dictionary given a list of indices and value
(10 answers)
Possible to set a Python nested dictionary item using single dot-delimited string path?
(2 answers)
Closed 11 months ago.
I have a number of dot notation strings and their values that I want to use to create a dictionary of dictionaries, but I'm stuck...
For example, I have these strings:
tagPaths = []
tagPaths.append("pump1.runtime")
tagPaths.append("pump1.volume")
tagPaths.append("pump2.runtime")
tagPaths.append("pump2.volume")
tagPaths.append("flowmeter.pv")
tagPaths.append("flowmeter.total")
tagPaths.append("test.testa.testb")
And I have values for each of these strings:
tagValues = []
tagValues.append(10.5)
tagValues.append(256)
tagValues.append(32)
tagValues.append(700)
tagValues.append(5.6)
tagValues.append(900)
tagValues.append("test value")
The resulting dictionary I want to produce is:
{
"pump1": {
"runtime": 10.5,
"volume": 256
},
"pump2": {
"runtime": 32,
"volume": 700
},
"flowmeter": {
"pv": 5.6,
"total": 900
},
"test": {
"testa": {
"testb": "test value"
}
}
}
}
I'm able to create a single string as a nested dictionary with its value assigned, but no idea how to merge them all into a single dictionary of nested dictionaries.
e.g. from this post:
How to create a nested dictionary from a list in Python?
tagPaths = []
tagPaths.append("pump1.runtime")
tagPaths.append("pump1.volume")
tagPaths.append("pump2.runtime")
tagPaths.append("pump2.volume")
tagPaths.append("flowmeter.pv")
tagPaths.append("total.volume")
tagPaths.append("test.testa.testb")
tagValues = []
tagValues.append(10.5)
tagValues.append(256)
tagValues.append(32)
tagValues.append(700)
tagValues.append(5.6)
tagValues.append(900)
tagValues.append("test value")
tags = {}
for path, value in zip(tagPaths, tagValues):
tag = {}
for i, pathElement in enumerate(reversed(path.split('.'))):
tag = {pathElement: tag}
# if we're at the last element in the tag path (i.e. the first reversed), then set the key value to the value of the tag
if i == 0:
tag[pathElement] = value
tags.update(tag) # <== This doesn't work as I expected and just replaces all of the nested dictionaries with the `tag` dictionary :/ I need this to additively update, not overwrite update.
Where I'm stuck is the tags.update(tag) part. This produces:
{
'pump1': {
'volume': 256 ## 'runtime' key is missing
},
'pump2': {
'volume': 700 ## 'runtime' key is missing
},
'flowmeter': {
'pv': 5.6 ## 'total' key is missing
},
'test': {
'testa': {
'testb': 'test value'
}
}
}
Note: I could have any number of dots in the string, not just 1 or 2 e.g.
area.subarea.pump1.status

How do I extract nested json names and convert to dot notation string list in python?

I need to pull data in from elasticsearch, do some cleaning/munging and export as table/rds.
To do this I have a long list of variable names required to pull from elasticsearch. This list of variables is required for the pull, but the issue is that not all fields may be represented within a given pull, meaning that I need to add the fields after the fact. I can do this using a schema (in nested json format) of the same list of variable names.
To try and [slightly] future proof this work I would ideally like to only maintain the list/schema in one place, and convert from list to schema (or vice-versa).
Is there a way to do this in python? Please see example below of input and desired output.
Small part of schema:
{
"_source": {
"filters": {"group": {"filter_value": 0}},
"user": {
"email": "",
"uid": ""
},
"status": {
"date": "",
"active": True
}
}
}
Desired string list output:
[
"_source.filters.group.filter_value",
"_source.user.email",
"_source.user.uid",
"_source.status.date",
"_source.status.active"
]
I believe that schema -> list might be an easier transformation than list -> schema, though am happy for it to be the other way round if that is simpler (though need to ensure the schema variables have the correct type, i.e. str, bool, float).
I have explored the following answers which come close, but I am struggling to understand since none appear to be in python:
Convert dot notation to JSON
Convert string with dot notation to JSON
Where d is your json as a dictionary,
def full_search(d):
arr = []
def dfs(d, curr):
if not type(d) == dict or curr[-1] not in d or type(d[curr[-1]]) != dict:
arr.append(curr)
return
for key in d[curr[-1]].keys():
dfs(d[curr[-1]], curr + [key])
for key in d.keys():
dfs(d, [key])
return ['.'.join(x) for x in arr]
If d is in json form, use
import json
res = full_search(json.loads(d))

Add a value to a pre-existing key in a dictionary? [duplicate]

This question already has answers here:
How can I add new keys to a dictionary?
(19 answers)
Closed 4 years ago.
I'm trying to add a third value to existing dictionary keys in Python. I'm sure this is probably easy, but I'm fairly new to coding so each way I've tried has thrown an error. Below is an example of what I tried which resulted in a syntax error. I also tried making the colon an equal sign but I got the message can't assign to a literal which I figure is due to the presence of two different = in the same line of code.
student = [
{ "name": "Kellie", "student_id": 12345},
{ "name": "James", "student_id": 39875},
{ "name": "Katie", "student_id": 24680},
]
student[0] = "lastName" : "Vee"
student[1] = "lastName" : "Tee"
student[2] = "lastName" : "Zee"
Try to add element like this:
student[0]["grade"] = "1"
It will add here: { "name": "Kellie", "student_id": 12345} new key "grade" and asign the value of 1.

Categories

Resources