Restrict adding bad entry to python dictionary? - python

We have sample Python dictionary as given below, which can be modify by multiple developers in our project.
What could be best way to avoid developers not to add other than given sample keys, also to avoid them to add incorrect zip entry in address of person( zip should validate through post codes then allow to add zip), etc. also there is no currency exists with 'AAA' its invalid key, also having invalid value, I think we can do it with special functions dict.add dict.key is there any other best way to do it.
people = {1: {'name': 'John', 'age': '27', 'sex': 'Male', 'Address': { 'Door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip':'99705'}, 'ExchangeCurrency':{'USD':'INR'}},
2: {'name': 'Marie', 'age': '22', 'sex': 'Female', 'Address': { 'Door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip': '99705'}, 'ExchangeCurrency':{'INR':'EUR'}}}
Eg of bad entry:
{1: {'name12': 'John', 'age': '27', 'sex': 'Male', 'Address': { 'Door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip':'000000'}, 'ExchangeCurrency':{'AAA':'CCC'}}

One approach that you might want to consider is creating your own class for each of the people for which the "given sample keys" (e.g. name, age, sex, and address) are object attributes and keyword arguments to the initialization function. For instance, you could do the following.
class Person(object):
def __init__(self, name = '', age = '0', sex = None, address = {}):
self.name = name
self.age = age
self.sex = sex
self.address = address
With that, you can enter person one into the system with
person_1 = Person(name = 'John',
age = '27',
sex = 'Male',
address = { 'Door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip':'99705'})
In fact, you can very nicely unpack your dictionary version of a person to create this a Person instance. For example, we can change your dictionary of dictionaries to a dictionary of Person objects as follows.
people_dic_1 = {1: {'name': 'John', 'age': '27', 'sex': 'Male', 'address': { 'door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip':'99705'}},
2: {'name': 'Marie', 'age': '22', 'sex': 'Female', 'address': { 'door': '9-8-11', 'street': 'John-street', 'city': 'NewYork', 'zip': '99705'}}}
people_dic_2 = {k:Person(**v) for k,v in people_dic.items()}
In the above, Person(**v) is a person object with attributes determined by the dictionary v. Notably, calling Person(**v) will only work if each key corresponds to a (correctly named) keyword argument of the __init__ method.
To go the other direction, you can call vars on a Person object to produce the kind of dictionary that you've been using. For instance, calling vars(people_dic_2[2]) after running the above code yields the dictionary
{'name': 'Marie',
'age': '22',
'sex': 'Female',
'address': {'door': '9-8-11',
'street': 'John-street',
'city': 'NewYork',
'zip': '99705'}}
You could similarly create a class for addresses, if you're so inclined.

Related

update values in a list of dictionaries

I have the following list:
list_dict = [
{'name': 'Old Ben', 'age': 71, 'country': 'Space', 'hobbies': ['getting wise']},
{'name': 'Han', 'age': 26, 'country': 'Space', 'hobbies': ['shooting']},
{'name': 'Luke', 'age': 24, 'country': 'Space', 'hobbies': ['being arrogant']},
{'name': 'R2', 'age': 'unknown', 'country': 'Space', 'hobbies': []}
]
I would like to add a hobby to R2:
for i in range(len(list_dict)):
people = list_dict[i]
if people['name'] == 'R2':
people['hobbies'] = ['lubrication']
print(list_dict)
I got what I was expecting but as a newbie I'd like to learn a few easy tricks to make it shorter.
I'd express as:
people = {person['name']: person for person in list_dict}
people['R2']['hobbies'] += ['lubrication'] # reads nicely as "add a hobby to R2"
For all those hung up on the academic concern of "potential people with same name", although the "Leetcode answer" would be:
for person in list_dict:
if person['name'] == 'R2':
person['hobbies'] += ['lubrication']
But in practice, remodeling data to have & use primary keys is probably what you want in most cases.
You can just iterate over the list and condense the if statement:
for person in list_dict:
person['hobbies'] = ['lubrication'] if person['name'] == 'R2' else person['hobbies']
There are some other answers here but in my eyes they don't really help a newbie seeking to shorten his code. I don't suggest to use the below proposed shortening except the looping over items in list instead of using indices, but to give here an answer with some maybe worth to know 'tricks' (you can have more than one name pointing to same variable/object, you can iterate directly over items of a list instead using indices) to a newbie here a shortened version of the code:
for p in l:
if p[n]=='R2': p[h]=['lubrication']
print(l)
and below all of the code using the proposed shortening with comments pointing out the 'tricks':
list_dict = [
{'name': 'Old Ben', 'age': 71, 'country': 'Space', 'hobbies': ['getting wise']},
{'name': 'Han', 'age': 26, 'country': 'Space', 'hobbies': ['shooting']},
{'name': 'Luke', 'age': 24, 'country': 'Space', 'hobbies': ['being arrogant']},
{'name': 'R2', 'age': 'unknown', 'country': 'Space', 'hobbies': []},
{'name': 'R2', 'age': 15, 'country': 'Alduin', 'hobbies': []}
]
l = list_dict; n='name'; h='hobbies'
# I would like to add a hobby to R2:
"""
for i in range(len(list_dict)):
people = list_dict[i]
if people['name'] == 'R2':
people['hobbies'] = ['lubrication']
"""
# I got what I was expecting but as a newbie I'd like to learn a few
# easy tricks to make it shorter:
for p in l: # loop over items in list instead of using indices
if p[n]=='R2': p[h]=['lubrication'] # use short variable names
# ^-- give --^ long string constants short names
print(l)
there is no need to loop over the lenght, you can loop through the list and you can condense with a one-liner if statement
#!usr/bin/python3
from pprint import pprint
for person in list_dict:
person['hobbies'].append('lubrification') if person['name'] == 'R2' else ...
pprint(list_dict)
>>> [
{'name': 'Old Ben', 'age': 71, 'country': 'Space', 'hobbies': ['getting wise']},
{'name': 'Han', 'age': 26, 'country': 'Space', 'hobbies': ['shooting']},
{'name': 'Luke', 'age': 24, 'country': 'Space', 'hobbies': ['being arrogant']},
{'name': 'R2', 'age': 'unknown', 'country': 'Space', 'hobbies': ["lubrification"]}
]
you can also do this with a comprehension:
[person['hobbies'].append('lubrification') for person in list_dict if person['name']]
But, if you just want to change one, you can use this code:
m = next(i for i,person in enumerate(list_dict) if person["name"]=="R2")
list_dict[m]["hobbies"].append("lubrification")

how to use nested dictionary in python which return user defined variable

I am trying to fetch value of time if found in people it should print time otherwise without time
Below is code which I am trying to test:
import datetime
people = {1: {'time': '', 'age': '27', 'sex': 'Male'},
2: {'time': '', 'age': '22', 'sex': 'Female'}}
time = datetime.datetime.now()
for time in people.items():
if time in people.items():
print(people,"true");
else:
print(people,"false");
The output is :
{1: {'age': '27', 'sex': 'Male', 'time': ''}, 2: {'age': '22', 'sex': 'Female', 'time': ''}} true
{1: {'age': '27', 'sex': 'Male', 'time': ''}, 2: {'age': '22', 'sex': 'Female', 'time': ''}} true
I want it to display the value of current timestamp value in time.
Desired output:
{1: {'age': '27', 'sex': 'Male', 'time': '2021-06-17T05:42:32.204965'}, 2: {'age': '22', 'sex': 'Female', 'time': '2021-06-17T05:42:32.204965'}} true
Refrence:
{
"id": "urn:ngsi-ld:Device:filling001",
"type": "FillingSensor",
"filling": {
"type": "Property",
"value": 0.94,
"unitCode": "C62",
**"time": "2021-01-28T12:33:10.000Z"**
},
"time_index": "2021-01-28T12:33:20.476"
}
What i am doing wrong here. help on this will be great?
There are several problems with your code :
You use the same variable time with time = datetime.datetime.now() and for time in people.items() so the second will overwrite the first one value.
people.items() returns a tuple of key, value elements, ie: (1, {...}) so you cannot compare time with (key, value).
Using in operator for this seems quite wrong to me, I would try something like below.
.
time = datetime.datetime.now()
for key, val in people.items():
print({key:val}, "true" if val.get("time") == time else "false")
You need to initialize time as datetime.datetime.now() before the dictionary initialization, and then replace the empty quotes with time.

How to check if specific keys and values are also in a dictionary?

The following is a subset of a nested dictionary that I have:
data = {
'1': {'Address': '10/3 Beevers St',
'Age': '27',
'Job': 'Doctor',
'Married': 'No',
'Name': 'John',
'Sex': 'Male',
'Suburb': 'Marine'},
'2': {'Address': '11/2 Sayers St',
'Age': '22',
'Job': 'Lawyer',
'Married': 'Yes',
'Name': 'Marie',
'Sex': 'Female',
'Suburb': 'Raffles'},
'3': {'Address': '5/1 Swamphen St',
'Age': '24',
'Job': 'Manager',
'Married': 'No',
'Name': 'Luna',
'Sex': 'Female',
'Suburb': 'Eunos'},
'4': {'Address': '25/12 Swamphen St',
'Age': '35',
'Job': 'Teacher',
'Married': 'Yes',
'Name': 'Larry',
'Sex': 'Male',
'Suburb': 'Eunos'}
}
And here is a JSON string:
json_str = '[{"Suburb": "Marine", "Address": "3 Beevers St"},\
{"Suburb": "Raffles", "Address": "11/2 Sayers St"},\
{"Suburb": "Eunos", "Address": "Swamphen St"}]'
My task is to check if a house ("Suburb" and "Address") in json_str is also in the original dataset (nested dictionary called data). If it is, then add the key/value for 'Age' and 'Name' to the JSON string for that house.
The output looks something like this:
[{'Age': 27,
'Address': '10/3 Beevers St',
'Name': 'John',
'Suburb': 'Marine'},
{'Age': 22,
'Address': '11/2 Sayers St',
'Name': 'Marie',
'Suburb': 'Raffles'}]
Thus, I was wondering if I can get some help on how to approach this question? I have tried writing out a code, but I keep getting errors, so I believe my approach is very incorrect...
def add_additional_info(data, json_str):
python_dict = json.loads(json_str)
new_dict = {}
for some_id, sales_data in data.items():
for houses in python_dict:
if houses["Suburb"] == sales_data["Suburb"] and \
houses["Address"] == sales_data["Address"]:
new_dict[houses] = {'Age': sales_data['Age'],
'Address': sales_data['Address'],
'Name': sales_data['Name'],
'Suburb': sales_data['Suburb']}
else:
new_dict.remove(houses)
return new_dict.dumps
I think this does what you said you want, include the clarifications you mentioned in comments about ignoring duplicate matches and the specified ordering of the keys in the results.
First, the test data:
data = {
'1': {'Address': '10/3 Beevers St',
'Age': '27',
'Job': 'Doctor',
'Married': 'No',
'Name': 'John',
'Sex': 'Male',
'Suburb': 'Marine'},
'2': {'Address': '11/2 Sayers St',
'Age': '22',
'Job': 'Lawyer',
'Married': 'Yes',
'Name': 'Marie',
'Sex': 'Female',
'Suburb': 'Raffles'},
'3': {'Address': '5/1 Swamphen St',
'Age': '24',
'Job': 'Manager',
'Married': 'No',
'Name': 'Luna',
'Sex': 'Female',
'Suburb': 'Eunos'},
'4': {'Address': '25/12 Swamphen St',
'Age': '35',
'Job': 'Teacher',
'Married': 'Yes',
'Name': 'Larry',
'Sex': 'Male',
'Suburb': 'Eunos'}
}
json_str = '''[{"Suburb": "Marine", "Address": "3 Beevers St"},
{"Suburb": "Raffles", "Address": "11/2 Sayers St"},
{"Suburb": "Eunos", "Address": "Swamphen St"}]'''
The code:
SUBSET = 'Age', 'Address', 'Name', 'Suburb' # Keys from data to put in result.
def add_additional_info(data, json_str):
houses = json.loads(json_str)
updated_houses = []
for house in houses:
# Search original dataset for house and add it to results, but only if
# there's a single match.
found = None
for some_id, sales_data in data.items():
if(house["Suburb"] == sales_data["Suburb"] and
house["Address"] in sales_data["Address"]): # Might ignore unit number.
if not found: # First match?
found = {key: sales_data[key] for key in SUBSET}
else:
found = None # Ignore multiple matches.
break # Halt search for house.
if found:
updated_houses.append(found)
return json.dumps(updated_houses, indent=4)
result = add_additional_info(data, json_str)
print(result)
Printed results:
[
{
"Age": "27",
"Address": "10/3 Beevers St",
"Name": "John",
"Suburb": "Marine"
},
{
"Age": "22",
"Address": "11/2 Sayers St",
"Name": "Marie",
"Suburb": "Raffles"
}
]
The way you're doing it now is comparing the complete dict entry for the JSON data with the complete dict entry of the other data. This will never match as these have separate keys, so the dicts are by defenition different.
You need to compare exact keys for both dict entries, fe:
if houses['Address'] == sales_data['Address'] and houses['Suburb'] == sales_data['Suburb']:
Also, never delete entries from a dict you are looping through. This leads to weird results. Create a new dict to which you add the ones you need to return, and add the matching entries to that.

Iterating through a nested dictionary to check if a particular value is all numerical letters

the following is a subset of a really large nested dictionary that I have:
{
'1': {'Name': 'Katherine Watson',
'Age': '1',
'Height': '150'},
'2': {'Name': 'Emilia Li',
'Age': '56',
'Height': '175'},
'3': {'Name': 'Dorothy Johnson',
'Age': '29',
'Height': '162'},
'4': {'Name': 'Alexandar Knight',
'Age': '14',
'Height': '164r'}
}
I'm having trouble figuring out how to write a function that will iterate through the specific key ('Height'), which then returns the corresponding value if it's all numerical numbers or None otherwise.
E.g. the dictionary with ID'1' should return '150' for the height. But the dictionary with ID'4' should return None for the height.
Here's a code I've written but it only returns '150' instead of iterating through all the IDs and returning '150' '175' '162' 'None'.
data = {
'1': {'Name': 'Katherine Watson',
'Age': '1',
'Height': '150'},
'2': {'Name': 'Emilia Li',
'Age': '56',
'Height': '175'},
'3': {'Name': 'Dorothy Johnson',
'Age': '29',
'Height': '162'},
'4': {'Name': 'Alexandar Knight',
'Age': '14',
'Height': '164r'}
}
def person_height(height):
for some_id, info in data.items():
if info['Height'].isnumeric():
return info['Height']
else:
return None
Use isdigit
data = {
'1': {'Name': 'Katherine Watson',
'Age': '1',
'Height': '150'},
'2': {'Name': 'Emilia Li',
'Age': '56',
'Height': '175'},
'3': {'Name': 'Dorothy Johnson',
'Age': '29',
'Height': '162'},
'4': {'Name': 'Alexandar Knight',
'Age': '14',
'Height': '164r'}
}
def person_height(height):
if height.isdigit():
return height
for some_id, info in data.items():
print("\nID:", some_id)
print("Height:", person_height(info['Height']))
Output:
ID: 1
Height: 150
ID: 2
Height: 175
ID: 3
Height: 162
ID: 4
Height: None
You could also do this with a list comprehension.
def get_heights(data):
return [int(person['Height'])
if person['Height'].isdigit()
else None
for person in data.values()]
print(get_heights(data))
Running it with your sample data outputs:
[150, 175, 162, None]
Since you're not using the IDs, you can use .values() instead of .items(). And in your code, you named the argument height but then refer to data in the function body. This means that it doesn't matter what you supply as the argument; the code only works because it's referring back to the globally defined variable, which happens to have the same name.
I've also converted the heights to integers, even though you didn't specifically request that.
Your code is fine actually but return will break the loop immediately and return the first result only so just turn your return to print() will do the work.
Another way is save the result to a list first and read them later:
data = {
'1': {'Name': 'Katherine Watson',
'Age': '1',
'Height': '150'},
'2': {'Name': 'Emilia Li',
'Age': '56',
'Height': '175'},
'3': {'Name': 'Dorothy Johnson',
'Age': '29',
'Height': '162'},
'4': {'Name': 'Alexandar Knight',
'Age': '14',
'Height': '164r'}
}
def person_height(data):
height_list = []
for some_id, info in data.items():
if info['Height'].isnumeric():
height_list.append(info['Height'])
else:
height_list.append(None)
return height_list
for height in person_height(data):
print(height)
Output:
150
175
162
None
In order to store your results in a "clean" dictionary, you will need as many nested loops as nested dictionaries you have. So:
def check_height(your_dict):
new_clean_dict = {}
for a, b in your_dict.items():
for e, f in b.items():
if e == 'Height' and f.isdigit():
new_clean_dict[a] = {'Height': f}
else:
new_clean_dict[a] = {'Height': None}
return new_clean_dict
This will produce a new dictionary with the same root key and with a value for each key that is a nested dictionary with only the key Height.
In order to get the results:
new_clean_dict = check_height(your_dict)

How to order the keys of Dictionary

I have the below dictionary and I would like to print the keys in the order:
[Name,Gender,Occupation,Location]
{'Gender': 'Male',
'Location': 'Nizampet,Hyderabad',
'Name': 'Srikanth',
'Occupation': 'Data Scientist'}
Can someone suggest how it can be done.
You can arrange the order of keys when you create the dictionary.
Python 3.7+
In Python 3.7.0 the insertion-order preservation nature of
dict objects has been declared to be an official part of the Python
language spec. Therefore, you can depend on it.
old_dict = {'Gender': 'Male', 'Location': 'Nizampet,Hyderabad', 'Name': 'Srikanth', 'Occupation': 'Data Scientist'}
new_dict = {
"Name": old_dict["Name"],
"Gender": old_dict["Gender"],
"Occupation": old_dict["Occupation"],
"Location": old_dict["Location"]
}
print(new_dict)
Output
{'Name': 'Srikanth', 'Gender': 'Male', 'Occupation': 'Data Scientist', 'Location': 'Nizampet,Hyderabad'}
If you have a predetermined order in which you'd like to print the data, store the order in a list of keys and loop through them.
data = {
'Gender': 'Male',
'Location': 'Nizampet,Hyderabad',
'Name': 'Srikanth',
'Occupation': 'Data Scientist'
}
print_order = [
'Name',
'Gender',
'Occupation',
'Location',
]
for key in print_order:
print(f'{key}: {data[key]}')
Output:
$ python3 print_dict_in_order.py
Name: Srikanth
Gender: Male
Occupation: Data Scientist
Location: Nizampet,Hyderabad

Categories