I'm trying to create a dictionary from 3 lists and I'm having trouble with the values being overwritten.
My lists look like this:
hosts = ["Host1", "Host2" ...]
uptime = ["50", "60" ...]
downtime = ["50", "40" ...]
I need a dictionary that looks like this:
{"availability": [{"Host": "Host1", "Uptime": "50", "Downtime": "50"}, {"Host": "Host2", "Uptime": "60", "Downtime": "40"}]}
Here's my code:
availability_json = {"availability": {}}
for x in range(len(hosts)):
availability_json["availability"]["Host"] = hosts[x]
availability_json["availability"]["Uptime"] = uptime[x]
availability_json["availability"]["Downtime"] = downtime[x]
When I run it only the final list is included. I've also tried with dict comprehension with the same issue.
What you want is:
availability_json = {
"availability": [
{"Host": host, "Uptime": up, "Downtime": down}
for host, up, down in zip(hosts, uptime, downtime)
]
}
Your mistake is that you want to collect multiple instances of "availability", but you created a data structure that can hold only one of them.
You need a list. You even acknowledged this yourself,
I need a dictionary that looks like this:
{"availability": [{...}, {...}]}
where [ ... ] is the list. But in your code, you didn't create a list.
The adjustments you need to make in your code are:
initialize a dictionary containing a list, instead of a dictionary containing a dictionary
in every iteration of the loop, create a new "availability" dictionary (instead of reusing the same one each time), and append it to the list
availability_json = {"availability": []} # 1.
for x in range(len(hosts)):
a = {} # 2.
a["Host"] = hosts[x]
a["Uptime"] = uptime[x]
a["Downtime"] = downtime[x]
availability_json["availability"].append(a)
Of course, Python allows you to write the same more succinctly, as shown in orlp's answer.
Related
I want to create a dictionary with Key value pairs which are filled via an for Loop
The dictionary I want to achive
[
{
"timestamp": 123123123,
"image": "image/test1.png"
},
{
"timestamp": 0384030434,
"image": "image/test2.png"
}
]
My code does not work and I´m new to the datatypes of python.
images_dict = []
for i in images:
time = Image.open(i)._getexif()[36867]
images_dict = {"timestamp": time, "image": i}
What am I missing?
First, you seem to be confusing the definition of a list and a dictionary in python. Dictionaries use curly brackets {} and lists use regular brackets []. So in your first example, you are describing a list with a single element, which is a dictionary.
As for your code example, you are creating an empty list, and then iterating over images which I assume is a list of images, and then redefining the variable images_dict to be a dictionary with two key: value pairs for every iteration.
It seems like what you want is this:
images_dict = []
for image in images:
time = Image.open(1)._getexif()[36867]
images_dict.append({'timestamp': time, 'image': image})
The answer from Tom McLean worked for me, I´m a little bit confused with the dataypes of python
images_dict.append({"timestamp": time, "image": i})
I have a JSON file that looks like this:
{
"returnCode": 200,
"message": "OK",
“people”: [
{
“details: {
"first": “joe”,
“last”: doe,
“id”: 1234567,
},
“otheDetails”: {
“employeeNum”: “0000111222”,
“res”: “USA”,
“address”: “123 main street”,
},
“moreDetails”: {
“family”: “yes”,
“siblings”: “no”,
“home”: “USA”,
},
},
{
“details: {
"first": “jane”,
“last”: doe,
“id”: 987654321,
},
“otheDetails”: {
“employeeNum”: “222333444”,
“res”: “UK”,
“address”: “321 nottingham dr”,
},
“moreDetails”: {
“family”: “yes”,
“siblings”: “yes”,
“home”: “UK,
},
}
This shows two entries, but really there are hundreds or more. I do not know the number of entries at the time the code is run.
My goal is to iterate through each entry and get the 'id' under "details". I load the JSON into a python dict named 'data' and am able to get the first 'id' by:
data['people'][0]['details']['id']
I can then get the second 'id' by incrementing the '0' to '1'. I know I can set i = 0 and then increment i, but since I do not know the number of entries, this does not work. Is there a better way?
Less pythonic then a list comprehension, but a simple for loop will work here.
You can first calculate the number of people in the people list and then loop over the list, pulling out each id at each iteration:
id_list = []
for i in range(len(data['people'])):
id_list.append(data['people'][i]['details']['id'])
You can use dict.get method in a list comprehension to avoid getting a KeyError on id. This way, you can fill dictionaries without ids with None:
ids = [dct['details'].get('id') for dct in data['people']]
If you still get KeyError, then that probably means some dcts in data['people'] don't have details key. In that case, it might be better to wrap this exercise in try/except. You may also want to identify which dcts don't have details key, which can be gathered using error_dct list (which you can uncomment out from below).
ids = []
#error_dct = []
for dct in data['people']:
try:
ids.append(dct['details']['id'])
except KeyError:
ids.append(None)
#error_dct.append(dct)
Output:
1234567
987654321
I have dozens of lines to update values in nested dictionary like this:
dictionary["parent-key"]["child-key"] = [whatever]
And that goes with different parent-key for each lines, but it always has the same child-keys.
Also, the [whatever] part is written in unique manner for each lines, so the simple recursion isn't the option here. (Although one might suggest to make a separate lists of value to be assigned, and assign them to each dictionary entry later on.)
Is there a way do the same but in even shorter manner to avoid duplicated part of the code?
I'd be happy if it could be written something like this:
update_child_val("parent-key") = [whatever]
By the way, that [whatever] part that I'm assigning will be a long and complicated code, therefore I don't wish to use function such as this:
def update_child_val(parent_key, child_val):
dictionary[parent_key]["child-key"] = child_val
update_child_val("parent-key", [whatever])
Specific Use Case:
I'm making ETL to convert database's table into CSV, and this is the part of the process. I wrote some bits of example below.
single_item_template = {
# Unique values will be assigned in place of `None`later
"name": {
"id": "name",
"name": "Product Name",
"val": None
},
"price": {
"id": "price",
"name": "Product Price (pre-tax)",
"val": None
},
"tax": {
"id": "tax",
"name": "Sales Tax",
"val": 10
},
"another column id": {
"id": "another column id",
"name": "another 'name' for this column",
"val": "another 'val' for this column"
},
..
}
And I have a separate area to assign values to the copy of the dictionary single_item_template for the each row of source database table.
for table_row in table:
item = Item(table_row)
Item class here will return the copy of dictionary single_item_template with updated values assigned for item[column][val]. And each of vals will involve unique process for changing values in setter function within the given class such as
self._item["name"]["val"] = table_row["prod_name"].replace('_', ' ')
self._item["price"]["val"] = int(table_row["price_0"].replace(',', ''))
..
etcetera, etcetera.
In above example, self._item can be shortened easily by assigning it to variable, but I was wondering if I could also save the last five character ["val"].
(..or putting the last logic part as a string and eval later, which I really really do not want to do.)
(So basically all I'm saying here is that I'm lazy typing out ["val"], but I don't bother doing it either. Although I was still interested if there's such thing while I'm not even sure such thing exists in programming in general..)
While you can't get away from doing the work, you can abstract it away in a couple of different ways.
Let's say you have a mapping of parent IDs to intended value:
values = {
'name': None,
'price': None,
'tax': 10,
'[another column id]': "[another 'val' for this column]"
}
Setting all of these at once is only two lines of code:
for parent, val in values.items():
dictionary[parent]['val'] = val
Unfortunately there isn't an easy or legible way to transform this into a dict comprehension. You can easily put this into a utility function that will turn it into a one-line call:
def set_children(d, parents, values, child='val'):
for parent, values in zip(parents, values):
d[parent][child] = value
set_children(dictionary, values.keys(), values.values())
In this case, your values mapping will encode the transformations you want to perform:
values = {
'name': table_row["prod_name"].replace('_', ' '),
'price': int(table_row["price_0"].replace(',', '')),
...
}
I have a json file like:
{
"parentNode": {
"id": "root",
"mbs": [
16995.9859862176,
-6029.919928079834,
-4.6344976928710935,
4674.872691701428
]
},
"children": [
{
"id": "00t2",
"mbs": [
16561.761031809023,
-5189.992543469676,
5,
221.7414398051216
]
},
{
"id": "01t2",
"mbs": [
16851.244334748077,
-5189.992543469676,
5,
221.7414398051216
]
}
]
}
Now I want to change the mbs value but to take a log before for roll back.
so my code is like:
if jsondict['parentNode']:
mbsx=jsondict['parentNode']['mbs'][0]
mbsy=jsondict['parentNode']['mbs'][1]
nodeid=jsondict['parentNode']['id'] #log the node id and mbs value
jsondict['mymbs_parent']=[nodeid,mbsx,mbsy] #write em down
jsondict['parentNode']['mbs'][0]=mbsx+xoffset #then change the value
jsondict['parentNode']['mbs'][1]=mbsy+yoffset
That works fine for parent node,
but there maybe many children nodes, so for the children part, the code is like this:
if jsondict['children']:
count=len(jsondict['children'])
for i in range(count):
mbsx=jsondict['children'][i]['mbs'][0]
mbsy=jsondict['children'][i]['mbs'][1]
nodeid=jsondict['children'][i]['id']
jsondict['mymbs_children'][i]=(nodeid,mbsx,mbsy)
jsondict['children'][i]['mbs'][0]=mbsx+xoffset
jsondict['children'][i]['mbs'][1]=mbsy+yoffset
then I get list assignment index out of range error.
I guess there isn't mymbs_children in the json file yet so there isn't jsondict['mymbs_children'][i]
I haven't found out how to do it, any ideas?
I don't love your approach, but for simplicity I can answer the question you have asked. To fix the error you are getting, add
jsondict['mymbs_children'] = []
as the first line in the block after
if jsondict['children']:
This will create the list which you are then trying to write to.
Then you need to use .append() to add the items to the list:
jsondict['mymbs_children'].append((nodeid,mbsx,mbsy))
instead of
jsondict['mymbs_children'][i]=(nodeid,mbsx,mbsy)
you can put an if condition surrounding the for loop to check:
...
if count > 0:
for i in range(count):
...
for python 3.8 onwards, you can use walrus operator as well to cut down the number of lines of code: (for more info)
...
if (count := len(jsondict['children'])) > 0:
for i in range(count):
...
another way which I prefer is to not use range:
...
for child in jsondict['children']:
child['mbs'][blah]...
...
this will avoid the issue of out of range index, and you might not even need to use the count variable
# Key "mymbs_children" is not present in the dictionary yet,
# so you'll need to declare it first and initialise to an empty list.
jsondict['mymbs_children'] = []
if jsondict['children']:
count=len(jsondict['children'])
for i in range(count):
mbsx=jsondict['children'][i]['mbs'][0]
mbsy=jsondict['children'][i]['mbs'][1]
nodeid=jsondict['children'][i]['id']
# Use append to add the tuple to this list
jsondict['mymbs_children'].append((nodeid,mbsx,mbsy));
I have a JSON file, and what I am trying to do is getting this specific field '_id'. Problem is that when I use json.load('input_file'), it says that my variable data is a list, not a dictionary, so I can't do something like:
for value in data['_id']:
print(data['_id'][i])
because I keep getting this error: TypeError: list indices must be integers or slices, not str
What I also tried to do is:
data = json.load(input_file)[0]
It kinda works. Now, my type is a dictionary, and I can access like this: data['_id']
But I only get the first '_id' from the archive...
So, what I would like to do is add all '_id' 's values into a list, to use later.
input_file = open('input_file.txt')
data = json.load(input_file)[0]
print(data['_id'])# only shows me the first '_id' value
Thanks for the help!
[{
"_id": "5436e3abbae478396759f0cf",
"name": "ISIC_0000000",
"updated": "2015-02-23T02:48:17.495000+00:00"
},
{
"_id": "5436e3acbae478396759f0d1",
"name": "ISIC_0000001",
"updated": "2015-02-23T02:48:27.455000+00:00"
},
{
"_id": "5436e3acbae478396759f0d3",
"name": "ISIC_0000002",
"updated": "2015-02-23T02:48:37.249000+00:00"
},
{
"_id": "5436e3acbae478396759f0d5",
"name": "ISIC_0000003",
"updated": "2015-02-23T02:48:46.021000+00:00"
}]
You want to print the _id of each element of your json list, so let's do it by simply iterating over the elements:
input_file = open('input_file.txt')
data = json.load(input_file) # get the data list
for element in data: # iterate on each element of the list
# element is a dict
id = element['_id'] # get the id
print(id) # print it
If you want to transform the list of elements into a list of ids for later use, you can use list comprehension:
ids = [ e['_id'] for e in data ] # get id from each element and create a list of them
As you can see the data is a list of dictionaries
for looping over data you need to use the following code
for each in data:
print each['_id']
print each['name']
print each['updated']
it says that my variable data is a list, not a dictionary, so I can't do something like:
for value in data['_id']:
print(data['_id'][i])
Yes, but you can loop over all the dictionaries in your list and get the values for their '_id' keys. This can be done in a single line using list comprehension:
data = json.load(input_file)
ids = [value['_id'] for value in data]
print(ids)
['5436e3abbae478396759f0cf', '5436e3acbae478396759f0d1', '5436e3acbae478396759f0d3', '5436e3acbae478396759f0d5']
Another way to achieve this is using the map built-in function of python:
ids = map(lambda value: value['_id'], data)
This creates a function that returns the value of the key _id from a dictionary using a lambda expression and then returns a list with the return value from this function applied on every item in data