python dictionary update without overwriting - python

I am learning python, and I have two json files. The data structure in these two json files are different structures.
I start by importing both of the json files. I want to choose a course from the courses dict, and then add it to a specific education in the educations dict.
What I want to solve is via user input choose a key from the first dict, and then within a while loop, so I can add choose a key from the second dict to be added to the dict chosen from the first dict.
I am able to add the dict from the second dict to the one first as a sub dict as I want to, but with the update method it overwrites all previous values.
I have used the dict.update() method so not to overwrite previous values. I then want to write back the updated dict back to the first json file.
My code works partially, I am able to add a course to a educations, but it overwrites all previous courses I chose to add to a specific education.
This is the content of the first json file:
{
"itsak22": {
"edcuationId": "itsak22",
"edcuation_name": "cybersecurityspecialist"
},
"feu22": {
"edcuationId": "feu22",
"edcuation_name": "frontendutvecklare"
}
}
This is the content of the second json file:
{
"sql": {
"courseId": "itsql",
"course_name": "sql",
"credits": 35
},
"python": {
"courseId": "itpyt",
"course_name": "python",
"credits": 30
},
"agile": {
"courseId": "itagl",
"course_name": "agile",
"credits": 20
}
}
And this is my python code:
import json
# Load the first JSON file of dictionaries
with open('edcuations1.json') as f:
first_dicts = json.load(f)
# Load the second JSON file of dictionaries
with open('courses1.json') as f:
second_dicts = json.load(f)
# Print the keys from both the first and second JSON files
print("All educations:", first_dicts.keys())
print("All courses:", second_dicts.keys())
# Ask for input on which dictionary to add to which
first_key = input("Which education would you like to choose to add courses to? (Enter 'q' to quit): ")
while True:
second_key = input("Which course would you like to add to education? (Enter 'q' to quit)")
if second_key == 'q':
break
# Create a sub-dictionary named "courses" in the specific dictionary of the first file
if "courses" not in first_dicts[first_key]:
first_dicts[first_key]["courses"] = {}
first_dicts[first_key]["courses"].update(second_dicts[second_key])
#first_dicts = {**first_dicts, **second_dicts}
#first_dicts.update({'courses': second_dicts})
# Update the first JSON file with the new dictionaries
with open('edcuations1.json', 'w') as f:
json.dump(first_dicts, f, indent=4)

Here is my approach:
import json
# Load the first JSON file of dictionaries
with open("educations1.json") as f:
educations = json.load(f)
# Load the second JSON file of dictionaries
with open("courses1.json") as f:
courses = json.load(f)
# Print the keys from both the first and second JSON files
print("All educations:", educations.keys())
print("All courses:", courses.keys())
# Ask for input on which dictionary to add to which
education_key = input(
"Which education would you like to choose to add courses to? (Enter 'q' to quit): "
)
education_courses = educations[education_key].setdefault("courses", {})
while True:
course_key = input(
"Which course would you like to add to education? (Enter 'q' to quit): "
)
if course_key == "q":
break
education_courses[course_key] = courses[course_key]
# Update the first JSON file with the new dictionaries
with open("educations1.json", "w") as stream:
json.dump(educations, stream, indent=4)
A few notes
I fixed the typos: edcuations1.json -> educations1.json
Instead of generic names such as first_dicts, first_keys, ... I use more descriptive names
How it works
The heart of my solution is on this line:
education_courses = educations[education_key].setdefault("courses", {})
Which is the equivalent of:
if "courses" not in educations[education_key]:
educations[education_key]["courses"] = {}
education_courses = educations[education_key]["courses"]
The setdefault method basically assign a value (an empty dictionary in this case) to a dictionary if the key ("courses" in this case) is absent.

I'm not entirely sure how your desired result should look like but I think your dictionary courses should be a list and not a dictionary.
Then you can do
if "courses" not in first_dicts[first_key]:
first_dicts[first_key]["courses"] = []
first_dicts[first_key]["courses"].append (second_dicts[second_key])
And your result looks like this if you add all courses to itsak22
{
"itsak22": {
"edcuationId": "itsak22",
"edcuation_name": "cybersecurityspecialist",
"courses": [
{
"courseId": "itsql",
"course_name": "sql",
"credits": 35
},
{
"courseId": "itpyt",
"course_name": "python",
"credits": 30
},
{
"courseId": "itagl",
"course_name": "agile",
"credits": 20
}
]
},
"feu22": {
"edcuationId": "feu22",
"edcuation_name": "frontendutvecklare"
}
}

Related

Create dictionary using JSON data

I have a JSON file that has movie data in it. I want to create a dictionary that has the movie title as the key and a count of how many actors are in that movie as the value. An example from the JSON file is below:
{
"title": "Marie Antoinette",
"year": "2006",
"genre": "Drama",
"summary": "Based on Antonia Fraser's book about the ill-fated Archduchess of Austria and later Queen of France, 'Marie Antoinette' tells the story of the most misunderstood and abused woman in history, from her birth in Imperial Austria to her later life in France.",
"country": "USA",
"director": {
"last_name": "Coppola",
"first_name": "Sofia",
"birth_date": "1971"
},
"actors": [
{
"first_name": "Kirsten",
"last_name": "Dunst",
"birth_date": "1982",
"role": "Marie Antoinette"
},
{
"first_name": "Jason",
"last_name": "Schwartzman",
"birth_date": "1980",
"role": "Louis XVI"
}
]
}
I have the following but it's counting all of the actors from all of the movies instead of each movie and the number of actors per movie. I'm not sure how to do this correctly as I'm newer to Python so help would be great.
import json
def actor_count(json_data):
with open("movies_db.json", 'r') as file:
data = json.load(file)
for t in data:
title = [t['title'] for t in data]
for element in data:
for actor in element['actors']:
rolee = [actor['role'] for movie in data for actor in movie['actors']]
len_role = [len(role)]
newD = dict(zip(title, len_role))
print(newD)
json_data = open('movies_db.json')
actor_count(json_data)
You show json that only contains a dictionary, yet you seem to process it as if it were a list of dictionaries with the structure you have shown. Pending clarification, I am answering here as if the latter is true -- you have a list of dictionaries, since you would be asking a different question about a different error if this was not the case.
In your function, each element of data is a dictionary that contains the information for a single movie. To get a dict correlating the title to the count of actors in this movie, you just need to access the "title" key and the length of the "actors" key for each element.
def actor_count(json_data):
movie_actors = {}
for movie in json_data:
title = movie["title"]
num_actors = len(movie["actors"])
movie_actors[title] = num_actors
return movie_actors
Alternatively, use a dictionary comprehension to build this dictionary:
def actor_count(json_data):
movie_actors = {movie["title"]: len(movie["actors"]) movie in json_data}
return movie_actors
Now, load your json file once, and use that when you call actors_count. This will return a dictionary mapping each movie title to the number of actors.
with open("movies_db.json", 'r') as file:
data = json.load(file)
actors_count(data)
Note that loading the json file again in the function is unnecessary, since you already did it before calling the function, and are passing the parsed object to the function.
If you want to keep your current logic of using list comprehensions, and then zipping the resultant lists to create a dict, that is also possible although slightly less efficient. There are significant changes you will need to make:
def actor_count(json_data):
title = [t['title'] for t in json_data]
n_actors = [len(t['actors'] for t in json_data)]
newD = dict(zip(title, n_actors))
return newD
As before, no need to read the file again in the function
You're already looping over all elements in json_data as part of the list comprehension, so no need for another loop outside this.
You can get the number of actors simply by len(t['actors'])
You seem to have misconceptions about how list comprehensions and loops work. A list comprehension is a self-contained loop that builds a list. If you have a list comprehension, there's usually no need to surround it by the same for ... in ... statement that already exists in the comprehension.
def actor_count(json_data):
newD = dict()
with open("movies_db.json", 'r') as file:
data = json.load(file)
for t in data:
if t == 'title':
title_ = json_data[t]
newD[ title_ ] = 0
if t == 'actors':
newD[ title_ ] = len(json_data[t])
print(newD)
Output:
{'Marie Antoinette': 2}

Looking to export JSON in Python using multiple nested blocks

I am trying to build out a JSON export mirrored on this example:
import json
data = {}
data['people'] = []
data['people'].append({
'name': 'Scott',
'website': 'stackabuse.com',
'from': 'Nebraska'
})
with open('data.txt', 'w') as outfile:
json.dump(data, outfile)
However, I wish for there to be more nested {} than just the one above, but cannot seem to figure out how to do that. This is what I'd like the JSON to look like:
{
"people":{
"Citizens":{
"Workers":{
"wage":"34",
"id":"1 of 2"
},
"non-workers":{
"wage":"0",
"id":"2 of 2"
}
}
}
}
Can anyone help me? Thank you!
While the method Brenden Price pointed out works, there is no need for so many statements. The number of statements solely depends on how deep the dictionaries which you are assigning are.
At its shortest:
data = {}
data["people"] = {
"Citizens": {
"Workers": {"wage": "34", "id": "1 of 2"},
"Non-Workers": {"wage": "0", "id": "2 of 2"}
}
}
To create the nested {}, you're adding more dictionaries. I'm not sure the goal of this, but o achieve that exact example you've provided, your code would be something along the lines of this:
data = {}
data['people'] = {} # empty dict of people
data['people']['citizens'] = {} # empty dict of workers inside people
data['people']['citizens']['workers'] = {} # empty dict of workers inside people
data['people']['citizens']['non-workers'] = {} # empty dict of non-workers inside people
data['people']['citizens']['workers']['wage'] = "34" # set workers wage to str "34"
data['people']['citizens']['workers']['id'] = "1 of 2" # set id to str "1 of 2"
data['people']['citizens']['non-workers']['wage'] = "0" # set wage to "0"
data['people']['citizens']['non-workers']['id'] = "2 of 2" # set id to "2 of 2"
Depending on the usage, you could technically do this and create that exact example as well:
data = {
"people":{
"Citizens":{
"Workers":{
"wage":"34",
"id":"1 of 2"
},
"non-workers":{
"wage":"0",
"id":"2 of 2"
}
}
}
}

How to store what key of a JSON to be parsed by a script in another JSON?

Suppose I have a JSON called jsondata.json:
{
"apiURL": [
{
"name":"Target",
"url":"https://redsky.target.com/v2/plp/collection/13562231,14919690,13728423,14919033,13533833,13459265,14917313,13519319,13533837,14919691,13479115,47778362,15028201,51467685,50846848,50759802,50879657,13219631,13561421,52062271,14917361,51803965,13474244,13519318?key=eb2551e4accc14f38cc42d32fbc2b2ea&pricing_store_id=2088&multichannel_option=basics&storeId=321"
},
{
"name":"Safeway",
"url":"https://shop.safeway.com/bin/safeway/product/aemaisle?aisleId=1_23_2&storeId=1483"
}
]
}
I want to tell my script to retrieve data from the API the url contains as follows:
# Load JSON containing URLs of APIs of grocery stores
with open(json_data, 'r') as data_f:
data_dict = json.load(data_f)
# Organize API URLs
for apiurl in data_dict['apiURL']:
responses.append('')
responses[index] = requests.get(apiurl['url'])
responses[index].raise_for_status()
storenames.append(apiurl['name'])
index += 1
first_target_item = responses[0].json()['search_response']['items']['Item'][0]['title']
first_safeway_item = responses[1].json()['productsinfo'][0]['description']
As you can see, my current implementation requires me to manually enter to my script which key to parse from each API (last two lines). I want to eventually be able to retrieve information from a dynamic number of grocery stores, but each website stores data on their items in a different key of their API.
How can I automate the process (e.g. store the key to parse from in jsondata.json) so that I don't have to update my script every time I add a new grocery store?
If you are okay with modifying the jsondata.json you can keep an array like this:
{
"name":"Target",
"accessKeys": ["search_response", "items", "Item", "0", "title"],
"url":"https://redsky.target.com/v2/plp/collection/13562231,14919690,13728423,14919033,13533833,13459265,14917313,13519319,13533837,14919691,13479115,47778362,15028201,51467685,50846848,50759802,50879657,13219631,13561421,52062271,14917361,51803965,13474244,13519318?key=eb2551e4accc14f38cc42d32fbc2b2ea&pricing_store_id=2088&multichannel_option=basics&storeId=321",
}
In your python code:
keys=["search_response", "items", "Item", "0", "title"] #apiUrl['accessKeys']
target_item=responses[0].json()
for i in target_item:
target_item=target_item[i]
You can automate more,
def get_keys(data, keys):
for key in keys:
data=data[key]
return data
items=[]
for index, apiurl in enumerate(data_dict['apiURL']):
responses.append('')
responses[index] = requests.get(apiurl['url'])
responses[index].raise_for_status()
storenames.append(apiurl['name'])
items.append(get_keys(responses[index].json(), apiUrl['accessKeys']))

how to get specific value in python dictionary?

I call an api via python and return this code as response:
{
"cast": [
{
"character": "Power",
"name": "George"
},
{
"character": "Max",
"job": "Sound",
"name": "Jash"
},
{
"character": "Miranda North",
"job": "Writer",
"name": "Rebecca"
}
]
}
I am trying to get the value of Rebecca because i need to get the Writer.
So i wrote:
for person in cast # cast is the variable keeps whole code above NOT inside the dict:
if person["job"] == "Writer":
writer = person["name"]
but it gives me:
KeyError at search/15
u'job'
how can i get the value?
FULL CODE:
writer = ""
for person in api['cast']:
if person.get('job') == 'Writer':
writer = person.get('name')
return render(request, 'home.html', {
'writer': writer
})
home.html:
<p>{{writer}}</p>
That's because not all elements in the list have the job key.
Change to:
for person in cast #whole code above:
if person.get('job') == 'Writer':
writer = person.get('name')
One liner to find one writer.
writer = next((person for person in api['cast'] if person.get('job') == 'Writer'), None)
One liner to find all writers.
writers = [person for person in api['cast'] if person.get('job') == 'Writer']
Syntax for dictionary get() method:
dict.get(key, default=None)
Parameters
key: This is the Key to be searched in the dictionary.
default: This is the Value to be returned in case key does not exist.
You need to specify the default value for get in case the key doesn't exist.
>>> for person in api['cast']:
... if person.get('job', '') == 'Writer':
... writer = person.get('name')
person.get(u"job") == "Writer"
for person in cast["cast"]:
# cast is the variable keeps whole code above NOT inside the dict
if person["job"] == "Writer":
writer = person["name"]
try this
cast["cast"] == Value of Key "cast" , which in turn is list of Dicts
and for looping through each Dictionary as person

List Indices in json in Python

I've got a json file that I've pulled from a web service and am trying to parse it. I see that this question has been asked a whole bunch, and I've read whatever I could find, but the json data in each example appears to be very simplistic in nature. Likewise, the json example data in the python docs is very simple and does not reflect what I'm trying to work with. Here is what the json looks like:
{"RecordResponse": {
"Id": blah
"Status": {
"state": "complete",
"datetime": "2016-01-01 01:00"
},
"Results": {
"resultNumber": "500",
"Summary": [
{
"Type": "blah",
"Size": "10000000000",
"OtherStuff": {
"valueOne": "first",
"valueTwo": "second"
},
"fieldIWant": "value i want is here"
The code block in question is:
jsonFile = r'C:\Temp\results.json'
with open(jsonFile, 'w') as dataFile:
json_obj = json.load(dataFile)
for i in json_obj["Summary"]:
print(i["fieldIWant"])
Not only am I not getting into the field I want, but I'm also getting a key error on trying to suss out "Summary".
I don't know how the indices work within the array; once I even get into the "Summary" field, do I have to issue an index manually to return the value from the field I need?
The example you posted is not valid JSON (no commas after object fields), so it's hard to dig in much. If it's straight from the web service, something's messed up. If you did fix it with proper commas, the "Summary" key is within the "Results" object, so you'd need to change your loop to
with open(jsonFile, 'w') as dataFile:
json_obj = json.load(dataFile)
for i in json_obj["Results"]["Summary"]:
print(i["fieldIWant"])
If you don't know the structure at all, you could look through the resulting object recursively:
def findfieldsiwant(obj, keyname="Summary", fieldname="fieldIWant"):
try:
for key,val in obj.items():
if key == keyname:
return [ d[fieldname] for d in val ]
else:
sub = findfieldsiwant(val)
if sub:
return sub
except AttributeError: #obj is not a dict
pass
#keyname not found
return None

Categories