I can't seem to understand why is the following returning only the first result? when there are multiple students with that same name and I'm clearly looping through the dictionary. Any thoughts?
students = {
1: {
"name": "John",
"age": 18,
"hobbies": ["Programming", "Swimming", "working out"]
},
2: {
"name": "John",
"lol": "random",
"age": 18,
"hobbies": ["Programming, swimming, working out"]
},
3: {
"name": "Bob",
"age": 18,
"hobbies": ["Programming", "Swimming", "working out"]
},
}
#app.get("/get-student")
async def get_student_by_name(name : str):
for id in students:
if students[id]["name"] == name:
return students[id]
return {"Data": "Student not found"}
The returned result is always the first one in the dictionary.
This won't work since you're using 'return' statement inside the loop, if for example you are looking for 'john' the first time this parameter is found return is executed and function ends.
You could for example save those values and return them all, let me show you:
Instead of return, declare a list ids = []at the beginning of the function and every time john is found you add the id to the results list, ids.append(students[id]). Finally after the loop just return ids list, or if len(ids) is 0 just return None for error management.
Code example:
#app.get("/get-student")
async def get_student_by_name(name : str):
ids = []
for id in students:
if students[id]["name"] == name:
ids.append(students[id])
if len(ids) is 0:
return {"Data": "Student not found"}
else:
return ids
Related
I a trying to expose an API (if that's the correct way to say it). I am using Quart, a python library made out of Flask and this is what my code looks like:
async def capture_post_request(request_json):
for item in request_json:
callbackidd = item['callbackid']
print(callbackidd)
#app.route('/start_work/', methods=['POST'])
async def start_work():
content_type = request.headers.get('content-type')
if (content_type == 'application/json'):
request_json = await request.get_json()
loop = asyncio.get_event_loop()
loop.create_task(capture_post_request(request_json))
body = "Async Job Started"
return body
else:
return 'Content-Type not supported!'
My schema looks like that:
[
{
"callbackid": "dd",
"itemid": "234r",
"input": [
{
"type": "thistype",
"uri": "www.uri.com"
}
],
"destination": {
"type": "thattype",
"uri": "www.urino2.com"
}
},
{
"statusCode": "202"
}
]
So far what I am getting is this error:
line 11, in capture_post_request
callbackidd = item['callbackid']
KeyError: 'callbackid'
I've tried so many stackoverflow posts to see how to iterate through my list of dicts but nothing worked. At one point in my start_work function I was using the get_data(as_text=True) method but still no results. In fact with the last method (or attr) I got:
TypeError: string indices must be integers
Any help on how to access those values is greatly appreciated. Cheers.
Your schema indicates there are two items in the request_json. The first indeed has the callbackid, the 2nd only has statusCode.
Debugging this should be easy:
async def capture_post_request(request_json):
for item in request_json:
print(item)
callbackidd = item.get('callbackid')
print(callbackidd) # will be None in case of the 2nd 'item'
This will print two dicts:
{
"callbackid": "dd",
"itemid": "234r",
"input": [
{
"type": "thistype",
"uri": "www.uri.com"
}
],
"destination": {
"type": "thattype",
"uri": "www.urino2.com"
}
}
And the 2nd, the cause of your KeyError:
{
"statusCode": "202"
}
I included the 'fix' of sorts already:
callbackidd = item.get('callbackid')
This will default to None if the key isn't in the dict.
Hopefully this will get you further!
Edit
How to work with only the dict containing your key? There are two options.
First, using filter. Something like this:
def has_callbackid(dict_to_test):
return 'callbackid' in dict_to_test
list_with_only_list_callbackid_items = list(filter(has_callbackid, request_json))
# Still a list at this point! With dicts which have the `callbackid` key
Filter accepts some arguments:
Function to call to determine if the value being tested should be filtered out or not.
The iterable you want to filter
Could also use a 'lambda function', but it's a bit evil. But serves the purpose just as well:
list_with_only_list_callbackid_items = list(filter(lambda x: 'callbackid' in x, request_json))
# Still a list at this point! With dict(s) which have the `callbackid` key
Option 2, simply loop over the result and only grab the one you want to use.
found_item = None # default
for item in request_json:
if 'callbackid' in item:
found_item = item
break # found what we're looking for, stop now
# Do stuff with the found_item from this point.
Im new in python but always trying to learn.
Today I got this error while trying select a key from dictionary:
print(data['town'])
KeyError: 'town'
My code:
import requests
defworld = "Pacera"
defcity = 'Svargrond'
requisicao = requests.get(f"https://api.tibiadata.com/v2/houses/{defworld}/{defcity}.json")
data = requisicao.json()
print(data['town'])
The json/dict looks this:
{
"houses": {
"town": "Venore",
"world": "Antica",
"type": "houses",
"houses": [
{
"houseid": 35006,
"name": "Dagger Alley 1",
"size": 57,
"rent": 2665,
"status": "rented"
}, {
"houseid": 35009,
"name": "Dream Street 1 (Shop)",
"size": 94,
"rent": 4330,
"status": "rented"
},
...
]
},
"information": {
"api_version": 2,
"execution_time": 0.0011,
"last_updated": "2017-12-15 08:00:00",
"timestamp": "2017-12-15 08:00:02"
}
}
The question is, how to print the pairs?
Thanks
You have to access the town object by accessing the houses field first, since there is nesting.
You want print(data['houses']['town']).
To avoid your first error, do
print(data["houses"]["town"])
(since it's {"houses": {"town": ...}}, not {"town": ...}).
To e.g. print all of the names of the houses, do
for house in data["houses"]["houses"]:
print(house["name"])
As answered, you must do data['houses']['town']. A better approach so that you don't raise an error, you can do:
houses = data.get('houses', None)
if houses is not None:
print(houses.get('town', None))
.get is a method in a dict that takes two parameters, the first one is the key, and the second parameter is ghe default value to return if the key isn't found.
So if you do in your example data.get('town', None), this will return None because town isn't found as a key in data.
I am learning and having much fun with python, currently I am making a simple discord bot but I am stuck with nested dictionary access problem.
This is my command
#bot.command()
async def burek(ctx, arg):
burek_seller = burek_dictionary["bureks"][arg]["seller"]
burek_price = burek_dictionary["bureks"][arg]["price"]
burek_state = burek_dictionary["bureks"][arg]["state"]
burek_name = burek_dictionary["bureks"][arg]["name"]
await ctx.send(
f"{burek_name} is available {burek_state} at {burek_seller} for {burek_price}$."
)
The problem is I want to change 'arg' to search by 'name' in my nested dictionary not by a number of nested dictionary. I am aware I will have to make a few changes, but I have been stuck trying to figure it out for two days now :(
this is my dictionary
burek_dictionary = {
"bureks": {
"0": {
"name": "mesni",
"ingredient": "govedina",
"seller": "sipac",
"price": 2.2,
"state": "hot",
"demand": "low",
},
"1": {
"name": "sirni",
"ingredient": "sir",
"seller": "merc",
"price": 1.8,
"state": "cold",
"demand": "average",
},
"2": {
"name": "spinacni",
"ingredient": "spinaca",
"seller": "pecjak",
"price": 2,
"state": "fresh",
"demand": "high",
},
"3": {
"name": "ajdov",
"ingredient": "sirspinaca",
"price": 2.1,
"state": "hot",
"demand": "average",
},
}
}
Obviously now as 'arg' I have to write a number to achieve my result, but I would like to use 'name' from dictionary and achieve the same result. I have no idea idea how to approach this. I hope it makes sense! Thank you.
Sure.
Loop through the bureks and when you find the one with the matching name, use that.
I assume you might need the same functionality somewhere else, so I broke it out into a separate function.
def find_burek_by_name(name):
for burek_key, burek_info in burek_dictionary["bureks"].items():
if burek_info["name"] == name:
return burek_info
return None # Not found
#bot.command()
async def burek(ctx, arg):
burek_info = find_burek_by_name(arg)
if burek_info:
burek_seller = burek_info["seller"]
burek_price = burek_info["price"]
burek_state = burek_info["state"]
burek_name = burek_info["name"]
await ctx.send(
f"{burek_name} is available {burek_state} at {burek_seller} for {burek_price}$."
)
else:
pass # No such burek
If you know the entire dictionary beforehand, you can build a map from names to numbers, if the dict does not change you build the map in a single pass and avoid looping later:
name2num = {}
for num in burek_dictionary["bureks"]:
name2num[burek_dictionary["bureks"][num]["name"]] = num
print(name2num)
name = "sirni"
num = name2num[name]
burek_seller = burek_dictionary["bureks"][num]["seller"]
burek_price = burek_dictionary["bureks"][num]["price"]
burek_state = burek_dictionary["bureks"][num]["state"]
burek_name = burek_dictionary["bureks"][num]["name"]
print(burek_seller, burek_price, burek_state, burek_name)
Cheers!
Newbie here and stuck on returning some objects from JSON to my Flask API.
I have a list of dictionaries called data, you'll see in my code below. I need to check if the status_id is in the data. If it is, I have to display that user's name. How would I access a dictionary from a list? Or is my json not valid? I did use a linter to check and it passed the JSON test. I'm getting error: string indices must be integers. Which leads me to believe that since it's a list I'll need integers for the indexes.
Any help in the right direction would be great.
Here's my code:
#app.route("/status/<status_id>", methods=['GET'])
def get_status(status_id):
data = [{
"id": 5,
"name": "Meghan"
},
{
"id": 6,
"name": "Julia"
}
]
data_dump = json.dumps(data, indent=4)
if status_id in data_dump:
#find that status id and name and return it
return data_dump[status_id]['name']
else:
return "Not Found in Dictionary"
See below. A simplified version of the get_status function.
Pay attention to the HTTP status code (200 Vs. 404)
#app.route("/status/<status_id>", methods=['GET'])
def get_status(status_id):
data = [{
"id": 5,
"name": "Meghan"
},
{
"id": 6,
"name": "Julia"
}
]
name = None
for entry in data:
if entry['id'] == status_id:
name = entry['name']
break
if name is not None:
print('The name for status_id {} is {}'.format(status_id,name))
return name, 200
# or, if you want to return both use Flask jsonify and send a dict
# see http://flask.pocoo.org/docs/1.0/api/#flask.json.jsonify
else:
print('Can not find a name for status id {}'.format(status_id))
return "Not Found in Dictionary", 404
For me it seems like you want to return the name of the object where id == status_id. Is that right? Than you don't have to dump it to json. You can check if the status_id exists in the list with:
len(list(filter(lambda x: x['id'] == status_id, data))) == 1
Explanation:
list(filter(lambda x: x['id'] == status_id, data))
This filters your list of dictionaries to only have these dictionaries that have the matching id.
len(...) == 1
This checks if there is only and and exactly one object that has this id. If you want to return the name of exactly that dict you could write it like that:
matching_dict = list(filter(lambda x: x['id'] == status_id, data))
if len(matching_dict) == 1:
return matching_dict[0]['name']
And if you then want to return json (as a string). Then you have to call json.dumps() like json.dumps(matching_dict[0]) depending on whatever you want to do.
Edit: So putting all together it could look like that:
#app.route("/status/<status_id>", methods=['GET'])
def get_status(status_id):
data = [{
"id": 5,
"name": "Meghan"
},
{
"id": 6,
"name": "Julia"
}
]
matching_dict = list(filter(lambda x: x['id'] == status_id, data))
if len(matching_dict) == 1:
return json.dumps(matching_dict[0])
else:
return "Found zero or more than one in Dictionary"
Request: GET /status/5
Response: {"id":5, "name": "Meghan"}
I'm having trouble dynamically creating a Python dictionary path to loop through and validate a value. Here's what I'd like to do:
Make API call using Requests 1.0 and store the JSON response in a dict.
response = requests.get(path/to/file.json).json()
The response object will be formatted as follows:
{
"status": "OK",
"items": [
{
"name": "Name 1",
"id": 0,
"address":{
"city": "New York",
}
},
{
"name": "Name 2",
"id": 1,
"address":{
"city": "New York",
}
},
{
"name": "Name 3",
"id": 2,
"address":{
"city": "New York",
}
}]
}
Send the response dict, field and value to a function for validation. The function would take the response object and append the field entry to it to define its path then validate against the value. So in theory it would be:
response[field] = value
The code that I wrote to do this was:
def dynamic_assertion(response, field, value):
i = 0
stations = "response['items']"
count = len(response['items'])
while i < count:
path = '%s[%s]%s' % (stations, i, field)
path = path.strip("")
if path != value:
print type(path)
return False
i += 1
return True
dynamic_assertion(response, "['address']['city']", "New York")
I realize that once I create the path string it is no longer an object. How do I create this in a way that will allow me to keep the response object and append the reference path to traverse through? Is this even possible?!
I think you'd be better off avoiding a single path string in favor of a tuple or list of strings which represent the individual keys in the nested dictionaries. That is, rather than "['address']['city']" being your field argument, you'd pass ("address", "city"). Then you just need a loop to go through the keys and see if the final value is the correct one:
def dynamic_assertion(response, field, value):
for item in response["items"]:
for key in field:
item = item[key] # go deeper into the nested dictionary
if item != value:
return False # raising an exception might be more Pythonic
return True
Example output (given the response dict from the question):
>>> dynamic_assertion(response, ("address", "city"), "New York")
True
>>> dynamic_assertion(response, ("address", "city"), "Boston")
False
>>> response["items"][2]["address"]["city"] = "Boston" # make response invalid
>>> dynamic_assertion(response, ("address", "city"), "New York")
False
>>> dynamic_assertion(response, ("address", "city"), "Boston")
False