Python sort list of dictionaries - python

I have a list of dictionaries:
AccountValues = [
{'portfolio_ref': 1, 'tag': 'FullInit', 'value': '20642.95', 'currency': 'USD', 'percent': 0.0},
{'portfolio_ref': 1, 'tag': 'FullMaint', 'value': '21350.54', 'currency': 'USD', 'percent': 0.0},
{'portfolio_ref': 1, 'tag': 'NetLiq', 'value': '70976.05', 'currency': 'USD', 'percent': 100.0} ]
Simple mission per SQL description: Order by portfolio_ref ASC, percent DESC
What I tried unsuccessfully:
sorted(AccountsValues, key=lambda x: (x[1],-x[4]))
which gives me
KeyError: 1
Second attempt:
import operator
result = sorted(myAccountsValues, key=itemgetter('percent'))
which fails to sort on percentage.

You can use dict.__getitem__ or its syntactic sugar []:
res = sorted(AccountValues, key=lambda x: (x['portfolio_ref'], -x['percent']))
Remember that dictionaries are not indexable by integers. Historically (pre-3.6), they are not even ordered. Even in Python 3.7, you cannot directly extract the nth key or value.
Result:
print(res)
[{'portfolio_ref': 1, 'tag': 'NetLiq', 'value': '70976.05', 'currency': 'USD', 'percent': 100.0},
{'portfolio_ref': 1, 'tag': 'FullInit', 'value': '20642.95', 'currency': 'USD', 'percent': 0.0},
{'portfolio_ref': 1, 'tag': 'FullMaint', 'value': '21350.54', 'currency': 'USD', 'percent': 0.0}]

You just have to combine all the things you did correctly: sort keys as a tuple and the proper way of referencing a dict entry:
>>> sorted(AccountValues, key=lambda x: (x["portfolio_ref"], -x["percent"]))
[{'tag': 'NetLiq', 'portfolio_ref': 1, 'value': '70976.05', 'percent': 100.0, 'currency': 'USD'},
{'tag': 'FullInit', 'portfolio_ref': 1, 'value': '20642.95', 'percent': 0.0, 'currency': 'USD'},
{'tag': 'FullMaint', 'portfolio_ref': 1, 'value': '21350.54', 'percent': 0.0, 'currency': 'USD'}]
Better yet, use
sorted(AccountValues, key=itemgetter("portfolio_ref", "percent"))
Your first attempt failed because x[1] and x[4] are not valid references into the dictionaries: you have to use the labels you originally gave, not relative positions.
Your second attempt is deficient only because you don't have the secondary sort key.

Related

Populate Python dictionary with value in nested dictionary

I am using the AccuWeather RESTFul API to get the current weather conditions in the top 50 cities. One object of the JSON response looks like this:
{'Key': '28143', 'LocalizedName': 'Dhaka', 'EnglishName': 'Dhaka', 'Country': {'ID': 'BD', 'LocalizedName': 'Bangladesh', 'EnglishName': 'Bangladesh'}, 'TimeZone': {'Code': 'BDT', 'Name': 'Asia/Dhaka', 'GmtOffset': 6.0, 'IsDaylightSaving': False, 'NextOffsetChange': None}, 'GeoPosition': {'Latitude': 23.7098, 'Longitude': 90.40711, 'Elevation': {'Metric': {'Value': 5.0, 'Unit': 'm', 'UnitType': 5}, 'Imperial': {'Value': 16.0, 'Unit': 'ft', 'UnitType': 0}}}, 'LocalObservationDateTime': '2021-10-09T13:11:00+06:00', 'EpochTime': 1633763460, 'WeatherText': 'Mostly cloudy', 'WeatherIcon': 6, 'HasPrecipitation': False, 'PrecipitationType': None, 'IsDayTime': True, 'Temperature': {'Metric': {'Value': 32.2, 'Unit': 'C', 'UnitType': 17}, 'Imperial': {'Value': 90.0, 'Unit': 'F', 'UnitType': 18}}, 'MobileLink': 'http://www.accuweather.com/en/bd/dhaka/28143/current-weather/28143?lang=en-us', 'Link': 'http://www.accuweather.com/en/bd/dhaka/28143/current-weather/28143?lang=en-us'}
Now I want to populate a dictionary with 1) "EnglishName", 2) "WeatherText", and 3) "Temperature (Celsius)".
I do manage to get a key-value pair with "EnglishName" and "WeatherText" as below:
weatherResponse = result.json()
mydictionary = dict()
for p in weatherResponse:
print(p["EnglishName"])
print(p["LocalObservationDateTime"])
print(p["WeatherText"])
temp_C = list(p["Temperature"]["Metric"].values())[0]
print(f"Temperature in Celsius: {temp_C}")
print("--------")
mydictionary[p["EnglishName"]] = p["WeatherText"]
How can I assign the "temp_C" value of each key to the dictionary as well?
I tried the append function but that does not work.
Any help is appreciated!
Instead of adding only one value p["WeatherText"] to your dictionary you can add multiple through use of tuples, like (a,b). Please see the below line.
mydictionary[p["EnglishName"]] = (p["WeatherText"], p["Temperature"]["Metric"]["Value"])
This above line you can use to assign to your dictionary key multiple values, sample output from this:
{'Dhaka': ('Mostly cloudy', 32.2)}
You can read tuples just like lists
mydictionary["Dhaka"][0] # This for getting the text
mydictionary["Dhaka"][1] # This for getting the value
Also tuples may look similar to lists but in this case it is recommended to use tuples because lists should store same data type values and tuples can store multiple datatype values.
I want to populate a dictionary with 1) "EnglishName", 2) "WeatherText", and 3) "Temperature (Celsius)".
See below
data = [{
'Key': '28143',
'LocalizedName': 'Dhaka',
'EnglishName': 'Dhaka',
'Country': {
'ID': 'BD',
'LocalizedName': 'Bangladesh',
'EnglishName': 'Bangladesh'
},
'TimeZone': {
'Code': 'BDT',
'Name': 'Asia/Dhaka',
'GmtOffset': 6.0,
'IsDaylightSaving': False,
'NextOffsetChange': None
},
'GeoPosition': {
'Latitude': 23.7098,
'Longitude': 90.40711,
'Elevation': {
'Metric': {
'Value': 5.0,
'Unit': 'm',
'UnitType': 5
},
'Imperial': {
'Value': 16.0,
'Unit': 'ft',
'UnitType': 0
}
}
},
'LocalObservationDateTime': '2021-10-09T13:11:00+06:00',
'EpochTime': 1633763460,
'WeatherText': 'Mostly cloudy',
'WeatherIcon': 6,
'HasPrecipitation': False,
'PrecipitationType': None,
'IsDayTime': True,
'Temperature': {
'Metric': {
'Value': 32.2,
'Unit': 'C',
'UnitType': 17
},
'Imperial': {
'Value': 90.0,
'Unit': 'F',
'UnitType': 18
}
},
'MobileLink': 'http://www.accuweather.com/en/bd/dhaka/28143/current-weather/28143?lang=en-us',
'Link': 'http://www.accuweather.com/en/bd/dhaka/28143/current-weather/28143?lang=en-us'
}]
filtered_data = [{'EnglishName':e.get('EnglishName','NA'),'WeatherText':e.get('WeatherText','NA'),'temp_C':e.get('Temperature').get('Metric').get('Value')} for e in data]
print(filtered_data)
output
[{'EnglishName': 'Dhaka', 'WeatherText': 'Mostly cloudy', 'temp_C': 32.2}]

How to get/filter values in python3 json list dictionary response?

Below is result I got from API query.
[{'type':'book','title': 'example1', 'id': 12456, 'price': '8.20', 'qty': '12', 'status': 'available'},
{'type':'book','title': 'example2', 'id': 12457, 'price': '10.50', 'qty': '5', 'status': 'none'}]
How do I specify in code to get value pairs of title, price, & status only?
So result will be like:
[{'title': 'example1', 'price': '8.20', 'status': 'available'},
{'title': 'example2', 'price': '10.50', 'status': 'none'}]
You can use a dictionary comprehension within a list comprehension:
L = [{'type':'book','title': 'example1', 'id': 12456, 'price': '8.20', 'qty': '12', 'status': 'available'},
{'type':'book','title': 'example2', 'id': 12457, 'price': '10.50', 'qty': '5', 'status': 'none'}]
keys = ['title', 'price', 'status']
res = [{k: d[k] for k in keys} for d in L]
print(res)
[{'price': '8.20', 'status': 'available', 'title': 'example1'},
{'price': '10.50', 'status': 'none', 'title': 'example2'}]

Read large JSON dataset without writing down all specific names of every JSON sub section by using Python [duplicate]

This question already has answers here:
Iterating over dictionaries using 'for' loops
(15 answers)
Closed 4 years ago.
So I have the following JSON response that I get from a cloud database (this is a small part of it):
{'series': [{'name': 'G', 'type': 'c8y_g_ist', 'unit': ''},
{'name': 'T', 'type': 'c8y_t_rohr_ist', 'unit': 'C'},
{'name': 'P', 'type': 'c8y_ph_soll_ist', 'unit': 'ph'},
{'name': 'T', 'type': 'c8y_t_tank_ist', 'unit': 'C'},
{'name': 'V', 'type': 'c8y_v_rohr_ist', 'unit': 'm/s'},
{'name': 'Bio', 'type': 'c8y_NO3_Wert', 'unit': 'mg/l'},
{'name': 'T', 'type': 'c8y_t_rohrsurface_ist', 'unit': 'C'},
{'name': 'Bio', 'type': 'c8y_PO4_Wert', 'unit': 'mg/l'},
{'name': 'P', 'type': 'c8y_ph_ist', 'unit': 'ph'},
{'name': 'Bio', 'type': 'OD_Wert', 'unit': ''}],
'truncated': False,
'values': {'2018-03-15T00:00:17.000Z': [{'max': 92.78, 'min': 92.78},
{'max': 3.21, 'min': 3.21}],
'2018-03-15T00:05:18.000Z': [None, {'max': 3.2, 'min': 3.2}],
'2018-03-15T00:06:49.000Z': [{'max': 92.78, 'min': 92.78},
{'max': 3.2, 'min': 3.2},
{'max': 5, 'min': 5},
{'max': 3.64, 'min': 3.64},
{'max': 0, 'min': 0},
{'max': 0, 'min': 0},
{'max': 3.04, 'min': 3.04},
{'max': 0, 'min': 0},
{'max': 0, 'min': 0},
{'max': 0, 'min': 0}],
'2018-03-15T00:10:17.000Z': [{'max': 95.22, 'min': 95.22},
{'max': 3.14, 'min': 3.14},
None,
{'max': 3.57, 'min': 3.57},
{'max': 0.01, 'min': 0.01},
None,
{'max': 2.97, 'min': 2.97},
None,
None,
None],
'2018-03-15T00:15:17.000Z': [{'max': 92.78, 'min': 92.78},
{'max': 3.13, 'min': 3.13},
None,
None,
{'max': 0, 'min': 0},
None,
None,
None,
None,
None],
Now I can read a value, for example the 92.78 that is in this line:
'values': {'2018-03-15T00:00:17.000Z': [{'max': 92.78, 'min': 92.78},
With:
SingleValue = get_mint_json['values']['2018-03-15T00:00:17.000Z'][0]['max']
SingleValue = json.dumps(SingleValue)
pprint.pprint(SingleValue)
(here get_mint_json is what the JSON response is stored as) The response of this is as expected: '92.78'
Now my question: I want to do this for all the data points, so not only for 2018-03-15T00:00:17.000Z but also for 2018-03-15T00:05:18.000Z and 2018-03-15T00:06:49.000Z etc. How do I do that without having to hardcode all of these specific names?
What you need to do here is loop over every member of get_mint_json['values'] to get get_mint_json['values']['2018-03-15T00:00:17.000Z'], get_mint_json['values']['2018-03-15T00:05:18.000Z'], etc., and then, for each one, just access its [0][max].
Once you recognize that's your problem, it's pretty easy to write it up as a for loop:
for date, datedvalue in get_mint_json['values'].items():
finalvalue = datedvalue[0]['max']
do_something(finalvalue)
Or, since you don't need the key-value pairs here, just the values:
for datedvalue in get_mint_json['values'].values():
finalvalue = datedvalue[0]['max']
do_something(finalvalue)
However, notice that in your case, sometimes datedvalue[0] may not be a dict with a max key; it may be None. You'll probably want to handle that:
for datedvalue in get_mint_json['values'].values():
if datedvalue[0]:
finalvalue = datedvalue[0]['max']
do_something(finalvalue)
You can also collapse this into a list comprehension or generator expression:
finalvalues = (datedvalue[0]['max']
for datedvalue in get_mint_json['values'].values()
if datedvalue[0])
The same thing works if you need to loop over a list instead of a dict's keys/values/key-value pairs, of course. And it works with multiple levels of nested, just by writing one nested for loop for each level. For example, if you wanted every datedvalue[(anything)]['max'], not just datedvalue[0]['max']:
for datedvalue in get_mint_json['values'].values():
for individualvalue in datedvalue:
if individualvalue:
finalvalue = individualvalue['max']
do_something(finalvalue)
You can write that as a comprehension too, but at this point it's probably getting way too complex for it to be readable that way.
Another way to solve this problem is to use a library that offers "query paths", equivalent to XPath for XML (as used by ElementTree) or KVC paths for Objective C. One example is the dpath library, which uses XPath-style syntax:
finalvalues = dpath.util.search('values/*/0/max')
Or, for the doubly-nested version:
finalvalues = dpath.util.search('values/*/*/max')

Assigning values in looped dictionary

I am struggling with this loop where I want to assign values to a nested dictionary. I have a list called stp2 and a default dict that I created with none as values.
I am trying to take list elements one by one and assign to the dictionary.
stp2=['Anna', 'William', 'Mary', 'Ben', 'Richard','Calvin', 'Rock']
whereas my_dict is a default dict (nested) which looks like this:
defaultdict(<class 'dict'>, {0: {'name': None, 'price': None,}, 1:{'name': None, 'price': None,}, 2: {'name': None, 'price': None,}, 3: {'name': None, 'price': None,}, 4: {'name': None, 'price': None,}, 5: {'name': None, 'price': None,}, 6: {'name': None, 'price': None,}})
I am using this loop to update values:
for z in enumerate(stp2):
my_dict[z[0]]['name']=z[1]
print(my_dict)
My output should ideally look like this but I am getting "Rock" in every value repeated.
Ideal output:
{0: {'name': 'Anna', 'price': None,}, 1:{'name': 'William', 'price': None,}, 2: {'name': 'Mary', 'price': None,}, 3: {'name': 'Ben', 'price': None,}, 4: {'name': 'Richard', 'price': None,}, 5: {'name': 'Calvin', 'price': None,}, 6: {'name': 'Rock', 'price': None,}}
Appreciate your help.
Try the below code. you are not updating the key that's the reason you are getting the same value for all keys in dict.
stp2=['Anna', 'William', 'Mary', 'Ben', 'Richard','Calvin', 'Rock']
d = {
0: {'name': None, 'price': None},
1: {'name': None, 'price': None},
2: {'name': None, 'price': None},
3: {'name': None, 'price': None},
4: {'name': None, 'price': None},
5: {'name': None, 'price': None},
6: {'name': None, 'price': None}}
for index, value in enumerate(stp2):
d[index]['name'] = value
print d
# output:
{0: {'name': 'Anna', 'price': None},
1: {'name': 'William', 'price': None},
2: {'name': 'Mary', 'price': None},
3: {'name': 'Ben', 'price': None},
4: {'name': 'Richard', 'price': None},
5: {'name': 'Calvin', 'price': None},
6: {'name': 'Rock', 'price': None}}
In two lines
initializing empty dictionary
>>> dict={}
Updating dictionary using list comprehension
>>> for l1 in [{i:{'name':j,'price':None}} for i,j in enumerate(stp2)]:
dict.update(l1)
Final dictionary
>>> dict
{0: {'price': None, 'name': 'Anna'}, 1: {'price': None, 'name': 'William'}, 2: {'price': None, 'name': 'Mary'}, 3: {'price': None, 'name': 'Ben'}, 4: {'price': None, 'name': 'Richard'}, 5: {'price': None, 'name': 'Calvin'}, 6: {'price': None, 'name': 'Rock'}}
In your enumerate I believe z isn't pointing to the names the way you think it should.
my_dict = defaultdict(list)
tmp = {}
counter = 0
for person in stp2:
tmp = {'name': person, 'price': None}
my_dict[0] = tmp
tmp = {}
counter += 1
print(my_dict)

inserting new values in dicts stored in a list of dicts

[{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None},
I have a list of dicts like this and 3 lists that I zipped into one that has the values Id like to go into the corresponding dict.
I thought I could just go through the list and use a for loop to update the values but what ends up happening is that every dict is updated to the last item in the list of values
You can do this using your list of dicts. But as cᴏʟᴅsᴘᴇᴇᴅ noted, you don't need to initialize the empty structure. Here are two solutions, one with and one without pre-initialization.
Example data:
names = ["Alice", "Bob", "Carol", "Eve"]
price = [1.00, 2.01, 3.02, 4.03]
shares = [10, 50, 80, 100]
data = zip(names, price, shares)
With pre-initialization:
frame = [{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None},
{'name': None, 'price': None, 'shares': None}]
dlist = list(data)
for i, d in enumerate(frame):
for j, k in enumerate(d.keys()):
d[k] = dlist[i][j]
frame
[{'name': 'Alice', 'price': 1.0, 'shares': 10},
{'name': 'Bob', 'price': 2.01, 'shares': 50},
{'name': 'Carol', 'price': 3.02, 'shares': 80},
{'name': 'Eve', 'price': 4.03, 'shares': 100}]
Without pre-initialization:
fields = ["name", "price", "shares"]
[{k:v for k, v in zip(fields, d)} for d in data]

Categories