I have a nested dictionary and instead of a hard coded query to query for ownername John I want to prompt the user for a name and just to test it, I type John upon being prompted and the program crashes with an error that says string indices must be integers.
The below works fine and returns Kitty.
John = { 1: {'name': 'Kitty', 'kind': 'cat'},
2: {'name': 'Ruffle', 'kind': 'dog'},
3: {'name': 'Bobby', 'kind': 'fish'}
}
petownername = John[1]['name']
print(petownername)
This also works fine when I hardcode the owner_name to John and I input the dictionary query with f string.
John = { 1: {'name': 'Kitty', 'kind': 'cat'},
2: {'name': 'Ruffle', 'kind': 'dog'},
3: {'name': 'Bobby', 'kind': 'fish'}
}
owner_name = John
petname = f"{owner_name[1]['name']}"
print(petname)
But as mentioned above when I add new user input it stops working
John = { 1: {'name': 'Kitty', 'kind': 'cat'},
2: {'name': 'Ruffle','kind': 'dog'},
3: {'name': 'Bobby',' kind': 'fish'}
}
owner_name = str(input("Which pet owner do you want to look up?: "))
petname = f"{owner_name[1]['name']}"
print(petname)
I am getting this error:
Which pet owner do you want to look up?: John
Traceback (most recent call last):
File "test3.py", line 9, in <module>
petname = f"{owner_name[1]['name']}"
TypeError: string indices must be integers
So it works fine when I hardcode it and insert it via f string and input is clearly a string. Any idea why it's not working on user prompt?
Do I need to store and refer back to the user prompt in another way perhaps?
It's not working because in the second snippet, owner_name is a dict, while in the third snippet it's a string -- specifically owner_name = 'John' so owner_name[1] == 'o', and 'o'['name'] raises that TypeError. The f-string is irrelevant.
To fix it, use one more level of nested dicts. See How do I create a variable number of variables?.
By the way, instead of a dict with ints as keys, you might want to use a list instead.
pet_owners = {
"John": [
{'name': 'Kitty', 'kind': 'cat'},
{'name': 'Ruffle', 'kind': 'dog'},
{'name': 'Bobby',' kind': 'fish'},
]
}
owner_name = input('Pet owner name: ')
petname = pet_owners[owner_name][0]['name'] # Note the 0 instead of 1
print(petname)
Test run:
Pet owner name: John
Kitty
Also by the way, str(input()) is redundant in Python 3 since input() returns a string.
Related
I have a sample data frame below:
firstname
middlename
lastname
id
gender
salary
James
Smith
36636
M
3000
Michael
Rose
40288
M
4000
Robert
Williams
42114
M
4000
Maria
Anne
Jones
39192
F
4000
Jen
Mary
Brown
F
-1
Now I want to convert this into a JSON list like the below:
[{'firstname': 'James', 'middlename': '', 'lastname': 'Smith', 'id': '36636', 'gender': 'M', 'salary': 3000}, {'firstname': 'Michael', 'middlename': 'Rose', 'lastname': '', 'id': '40288', 'gender': 'M', 'salary': 4000}, {'firstname': 'Robert', 'middlename': '', 'lastname': 'Williams', 'id': '42114', 'gender': 'M', 'salary': 4000}, {'firstname': 'Maria', 'middlename': 'Anne', 'lastname': 'Jones', 'id': '39192', 'gender': 'F', 'salary': 4000}, {'firstname': 'Jen', 'middlename': 'Mary', 'lastname': 'Brown', 'id': '', 'gender': 'F', 'salary': -1}]
and I did that using the below code:
result = json.loads((df.toPandas().to_json(orient="records")))
Now what I want to do is, I want to send the JSON records one by one and hit the API. I can't send all the records at once and there are millions of records to be sent. So, how do I segregate these records using Map() or some other way so that it would work in a distributed fashion? It works well when I iterate a for loop on this list but takes time. So wanted to implement the most efficient way for this use case. The for loop code is as below:
for i in result_json:
try:
token = get_token(tokenUrl, tokenBody)
custRequestBody = {
"Token": token,
"CustomerName": "",
"Object": "",
"Data": [i]
}
#print("::::Customer Request Body::::::")
#print(custRequestBody)
response = call_to_cust_bulk_api(apiUrl, custRequestBody)
output = {
"headers": {
"Content-Type": "",
"X-Content-Type-Options": "",
"X-XSS-Protection": "",
"X-Frame-Options": "DENY",
"Strict-Transport-Security": ""
},
"body": {
"Response code": 200,
"ResponseMessage": response
}
}
Here, the result_json is already converted to the JSON list of records:
You can perform operation row-wise on your df using a udf (user defined function).
Spark will run this function on all executors in a distributed fashion
from pyspark.sql.types import IntegerType
from pyspark.sql.functions import udf
# Your custom function you want to run in pyspark
#udf(returnType=IntegerType())
def parse_and_post(*args):
print(args, type(args)) # args is of type typle
# Convert the args tuple to json
# Send the json to API
# Return a Status value based on API success of failure
"""if success:
return 200
else
return -1"""
df = spark.createDataFrame([(1, "John Doe", 21), (2, "Simple", 33)], ("id", "name", "age"))
# Apply the UDF to your Dataframe (called "df")
new_df = df.withColumn("post_status", parse_and_post( *[df[x] for x in df.columns] ))
Note
You might be tempted to call collect() function on your df and then iterate on rows but it will load all the data into the driver. Which beats the purpose of distributed computation.
Also the function will not be executed until you use/show the new_df since spark's lazy evaluation.
Read more about udf here
I'm trying to get two attributes at the time from my json data and add them as an item on my python list. However, when trying to add those two: ['emailTypeDesc']['createdDate'] it throws an error. Could someone help with this? thanks in advance!
json:
{
'readOnly': False,
'senderDetails': {'firstName': 'John', 'lastName': 'Doe', 'emailAddress': 'johndoe#gmail.com', 'emailAddressId': 123456, 'personalId': 123, 'companyName': 'ACME‘},
'clientDetails': {'firstName': 'Jane', 'lastName': 'Doe', 'emailAddress': 'janedoe#gmail.com', 'emailAddressId': 654321, 'personalId': 456, 'companyName': 'Lorem Ipsum‘}},
'notesSection': {},
'emailList': [{'requestId': 12345667, 'emailId': 9876543211, 'emailType': 3, 'emailTypeDesc': 'Email-In', 'emailTitle': 'SampleTitle 1', 'createdDate': '15-May-2020 11:15:52', 'fromMailList': [{'firstName': 'Jane', 'lastName': 'Doe', 'emailAddress': 'janedoe#gmail.com',}]},
{'requestId': 12345667, 'emailId': 14567775, 'emailType': 3, 'emailTypeDesc': 'Email-Out', 'emailTitle': 'SampleTitle 2', 'createdDate': '16-May-2020 16:15:52', 'fromMailList': [{'firstName': 'Jane', 'lastName': 'Doe', 'emailAddress': 'janedoe#gmail.com',}]},
{'requestId': 12345667, 'emailId': 12345, 'emailType': 3, 'emailTypeDesc': 'Email-In', 'emailTitle': 'SampleTitle 3', 'createdDate': '17-May-2020 20:15:52', 'fromMailList': [{'firstName': 'Jane', 'lastName': 'Doe', 'emailAddress': 'janedoe#gmail.com',}]
}
python:
final_list = []
data = json.loads(r.text)
myId = [(data['emailList'][0]['requestId'])]
for each_req in myId:
final_list.append(each_req)
myEmailList = [mails['emailTypeDesc']['createdDate'] for mails in data['emailList']]
for each_requ in myEmailList:
final_list.append(each_requ)
return final_list
This error comes up when I run the above code:
TypeError: string indices must be integers
Desired output for final_list:
[12345667, 'Email-In', '15-May-2020 11:15:52', 'Email-Out', '16-May-2020 16:15:52', 'Email-In', '17-May-2020 20:15:52']
My problem is definetely in this line:
myEmailList = [mails['emailTypeDesc']['createdDate'] for mails in data['emailList']]
because when I run this without the second attribute ['createdDate'] it would work, but I need both attributes on my final_list:
myEmailList = [mails['emailTypeDesc'] for mails in data['emailList']]
I think you're misunderstanding the syntax. mails['emailTypeDesc']['createdDate'] is looking for the key 'createdDate' inside the object mails['emailTypeDesc'], but in fact they are two items at the same level.
Since mails['emailTypeDesc'] is a string, not a dictionary, you get the error you have quoted. It seems that you want to add the two items mails['emailTypeDesc'] and mails['createdDate'] to your list. I'm not sure if you'd rather join these together into a single string or create a sub-list or something else. Here's a sublist option.
myEmailList = [[mails['emailTypeDesc'], mails['createdDate']] for mails in data['emailList']]
Strings in JSON must be in double quotes, not single.
Edit: As well as names.
Does return on python close the Loop for?
I'm asking that because I wrote a code that will search for me someone in a list and if know that person it will return me his age if not it will tell me "Person not found".
If the person is in the first place of the Array if find it but if it is in the second he doesn't why?
def lerDoBanco(name):
lista_names = [
{'name': 'Joaquim', 'age': 20},
{'name': 'João', 'age': 25},
{'name': 'Ana', 'age': 27}
]
for person in lista_names:
if person['name'] == name:
return person
else:
return {'name': 'Person not found', 'age': 0}
def fname(request, name):
result = lerDoBanco(name)
if result['age'] > 0:
return HttpResponse('The person was found, has ' + str(result['age']) + ' years old')
else:
return HttpResponse('Person not found')
I ask that because if I comment the else statement it shows me all the people in the Array correctly.
When you call return operator it will terminate the current function execution and return value. So if you will use return inside your loop, return will terminate the loop and the function and return some value.
return terminates execution further and returns some desired value (default is None). Change indentation to make your code working :
def lerDoBanco(name):
lista_names = [
{'name': 'Joaquim', 'age': 20},
{'name': 'João', 'age': 25},
{'name': 'Ana', 'age': 27}
]
for person in lista_names:
if person['name'] == name:
return person
return {'name': 'Person not found', 'age': 0}
It will iterate all the values to find the person then if not found then the default value {'name': 'Person not found', 'age': 0} will be returned.
From the moment you return you move out of the function, so out of the for loop as well.
What you thus can do is simply return the 'Person not found' at the end of the function:
def lerDoBanco(name):
lista_names = [
{'name': 'Joaquim', 'age': 20},
{'name': 'João', 'age': 25},
{'name': 'Ana', 'age': 27}
]
for person in lista_names:
if person['name'] == name:
return person
return {'name': 'Person not found', 'age': 0}
Note that if the data is stored in the database, it is better to make a queryset to filter at the database side. Databases are optimized to search effectively. You can also specify db_index=True on a column such that the database builds an index allowing much faster retrieval.
A loop ends when one of the following happens:
loop condition evaluates to false
break is used
an exception is raised
a return statement is executed
The points above are the same for every programming language
This question already has answers here:
Find the index of a dict within a list, by matching the dict's value
(12 answers)
Closed 3 years ago.
Say I have the following list of dicts:
dicts = [
{'name': "Tom", 'age': 20, 'height': 1.8},
{'name': "Isa", 'age': 31, 'height': 1.5},
... ]
I'd like to replace the age of a given person with a given value.
def replace_age(person, age):
dicts[?]['age'] = age
replace_age("Tom", 45)
Assuming that name is unique, what's the most elegant way to go about this?
In a golden world: dicts[name=person]['age'] = age
Not a duplicate of Find the index of a dict within a list, by matching the dict's value: I'm looking to change the value, not get the index. And Tom is a pretty common name.
this is a variant:
def replace_age(person, age):
try:
dct = next(item for item in dicts if item["name"] == person)
except StopIteration:
# person not found
# here you could print a message or raise an error...
return
dct["age"] = age
this will only work if the names are unique. if they are not only the first occurrence will be replaced.
since the name is unique you can change your data structure where you keep your data to achieve your task efficiently:
efficient_dict = {e['name']: {'age' : e.get('age'), 'height': e.get('height')} for e in dicts}
def replace_age(person, age):
if person in efficient_dict:
efficient_dict[person]['age'] = age
Here's my version
dictionaries = [
{'name': "Tom", 'age': 20, 'height': 1.8},
{'name': "Isa", 'age': 31, 'height': 1.5}
]
def change_dict_person_age(dictionaries, person, age):
for dictionary in dictionaries:
if dictionary['name'] == person:
dictionary['age'] = age
# Uncomment the following line if you want to stop at the 1st
# match. Leave it as is if you want to modify all occurrences.
#break
change_dict_person_age(dictionaries, "Tom", 40)
print(dictionaries)
#[{'name': 'Tom', 'age': 40, 'height': 1.8}, {'name': 'Isa', 'age': 31, 'height': 1.5}]
I also wrote a more generic version for broader user:
dictionaries = [
{'name': "Tom", 'age': 20, 'height': 1.8},
{'name': "Isa", 'age': 31, 'height': 1.5}
]
def change_dict(dictionaries, key_to_check, value_to_match, key_to_change, value_to_change):
for dictionary in dictionaries:
if dictionary[key_to_check] == value_to_match:
dictionary[key_to_change] = value_to_change
# Uncomment the following line if you want to stop at the 1st
# match. Leave it as is if you want to modify all occurrences.
#break
change_dict(dictionaries, "name", "Tom", "age", 50)
print(dictionaries)
#[{'name': 'Tom', 'age': 50, 'height': 1.8}, {'name': 'Isa', 'age': 31, 'height': 1.5}]
File1.py
john = {'fullname':'John Wick', 'Age': 35 , 'job':'Hitman'}
mark = {'fullname':'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'}
Will = {'fullname':'Will smith', 'Age': 45, 'job': 'Actor'}
so on... and many more names
So my question is how do I access the values, lets say I want to know the age of a person by giving the person's name?
File2.py
from File1.py import *
#what should the code be to access the age
#lets say I write 'john', I want to get his age
print john['Age'] # this is not how i want to access it
#
#any other way or methods ??
#
user_input=raw_input("enter a Name: ")
# user_input contains the name of the person I want to get the Age of
You can use a nested dictionary to pass the person's name to the dictionary and then access the age via the key "Age":
john = {'fullname':'John Wick', 'Age': 35 , 'job':'Hitman'}
mark = {'fullname':'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'}
Will = {'fullname':'Will smith', 'Age': 45, 'job': 'Actor'}
new_data = {"John":john, "Mark":mark, "Will":Will}
print(new_data["John"]["Age"])
Output:
35
Edit: Without a nested dictionary, you can try this:
john = {'fullname':'John Wick', 'Age': 35 , 'job':'Hitman'}
mark = {'fullname':'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'}
Will = {'fullname':'Will smith', 'Age': 45, 'job': 'Actor'}
user_input=raw_input("enter a Name: ")
final_age = [b["Age"] for a, b in globals().items() if a == user_input][0]
Output:
35
First of all in my opinion you should NOT do it like this. #Ajax1234 offers a much better solution, but to ask your question:
import File1
name = input('>>> Name: ')
print(getattr(File1, name)['Age']) # 35
Does it
You can use get function in dictionary
print(john.get('age' , 0))
get take two parameter first the key then the default value to load if key not found
You can use the built-in globals function. From documentation (emphasis mine):
Return a dictionary representing the current global symbol table. This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called).
Edit:
As you are using separate files, you can create a function in the file File1.py that accepts the dictionary name and returns the appropriate value using the globals() function.
Usage:
File1.py
john = {'fullname':'John Wick', 'Age': 35 , 'job':'Hitman'}
mark = {'fullname':'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'}
Will = {'fullname':'Will smith', 'Age': 45, 'job': 'Actor'}
def get_age(person):
return globals()[person]['Age']
File2.py
import File1
person = input('Enter name:')
print(File1.get_age(person))
I don't fully understand why you are saving each of these dicts into a separate variable instead of just using a nested dict like
persons = {
'john': {'fullname': 'John Wick', 'Age': 35, 'job': 'Hitman'},
'mark': {'fullname': 'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'},
'will': {'fullname': 'Will Smith', 'Age': 45, 'job': 'Actor'}
}
with which you could use user input to access the persons dict's keys,
if you really want to name your variables based on their values (which does not make much sense to me since it's not really variable after), you can use Python's globals() and locals() function.
file1.py
john = {'fullname': 'John Wick', 'Age': 35, 'job': 'Hitman'}
mark = {'fullname': 'Mark Zuckerberg', 'Age': 30, 'job': 'Developer'},
will = {'fullname': 'Will Smith', 'Age': 45, 'job': 'Actor'}
file2.py
from file1 import *
name = raw_input('Please enter a name:')
global_vars = globals()
print(global_vars[name])
But still, since you could just use a nested dict I see no single valid reason to use globals() for this use case at all. Just declare your dicts under keys in another dict and you should be just fine.
Next to a nested dictionary you can also use an import alias to make you code cleaner.
import File1 as employees
name = raw_input("Enter name:")
print(getattr(employees, name)['Age'])