Python3 asc desc sorting - python

Lets assume I've got a following python list(this is only example):
my_list = [{'user': 'Joe', 'score': 14},
{'user': 'Foo', 'score': 12},
{'user': 'May', 'score': 12},
{'user': 'Kat', 'score': 12},
{'user': 'Doe', 'score': 13}]
I need to sort this list in ascending order by score and descending order by a username.
Expected sort result:
my_list = [{'user': 'May', 'score': 12},
{'user': 'Kat', 'score': 12},
{'user': 'Foo', 'score': 12},
{'user': 'Doe', 'score': 13},
{'user': 'Joe', 'score': 14}]
So, I could do something like this if I want everything to be in ascending order:
my_list.sort(key=lambda x: (x['score'], x['user']))
For integers it is easy to solve this problem just adding - in front of it:
my_list.sort(key=lambda x: (-x['score'], x['user']))
Unfortunately, strings can not be negative :-|
I need a generic solution that doesn't involve 'reverse=True'. Lambda function is dynamically generated based on a user config.
Thoughts?

Your current solution will work fine if you set the reverse parameter of list.sort to True:
>>> my_list = [{'user': 'Joe', 'score': 14},
... {'user': 'Foo', 'score': 12},
... {'user': 'May', 'score': 12},
... {'user': 'Kat', 'score': 12},
... {'user': 'Doe', 'score': 13}]
>>> my_list.sort(key=lambda x: (-x['score'], x['user']), reverse=True)
>>> pprint(my_list) # pprint makes the nice output
[{'score': 12, 'user': 'May'},
{'score': 12, 'user': 'Kat'},
{'score': 12, 'user': 'Foo'},
{'score': 13, 'user': 'Doe'},
{'score': 14, 'user': 'Joe'}]
>>>
This will sort the list in reverse order.
Edit:
Since the names and scores have two different sort orders, you will need to use two separate sorts to achieve your desired output:
>>> my_list = [{'user': 'Joe', 'score': 14},
... {'user': 'Foo', 'score': 12},
... {'user': 'May', 'score': 12},
... {'user': 'Kat', 'score': 12},
... {'user': 'Doe', 'score': 13}]
>>> my_list.sort(key=lambda x: x['user'], reverse=True)
>>> my_list.sort(key=lambda x: x['score'])
>>> pprint(my_list)
[{'score': 12, 'user': 'May'},
{'score': 12, 'user': 'Kat'},
{'score': 12, 'user': 'Foo'},
{'score': 13, 'user': 'Doe'},
{'score': 14, 'user': 'Joe'}]
>>>

The ord of a string can be negative:
my_list.sort(key=lambda x: (x["score"],[-ord(x) for x in x["user"]])))
In [50]: my_list.sort(key=lambda x: (x["score"],[-ord(x) for x in x["user"]]))
In [51]: my_list
Out[51]:
[{'score': 12, 'user': 'May'},
{'score': 12, 'user': 'Kat'},
{'score': 12, 'user': 'Foo'},
{'score': 13, 'user': 'Doe'},
{'score': 14, 'user': 'Joe'}]

you need to add the reverse=True keyword for descending order.
>>> my_list.sort(key=lambda x: (-x['score'], x['user']),reverse=True)
>>> my_list
[{'score': 12, 'user': 'May'}, {'score': 12, 'user': 'Kat'}, {'score': 12, 'user': 'Foo'}, {'score': 13, 'user': 'Doe'}, {'score': 14, 'user': 'Joe'}]

Related

How can i get values from 2 lists and put them into a dictionary in python

I have 2 lists
list1 = ["ben", "tim", "john", "wally"]
list2 = [18,12,34,55]
the output im looking for is this
[{'Name': 'ben', 'Age': 18, 'Name': 'tim', 'Age': 12, 'Name': 'john', 'Age': 34, 'Name': 'wally', 'Age': 55}]
As mentioned in the comments, you can't have duplicate keys in a dictionary; even your output snippet would just return [{'Name': 'wally', 'Age': 55}]
However, {k: v for k, v in zip(list1, list2)} will return
{'ben': 18, 'tim': 12, 'john': 34, 'wally': 55}
And [{'Name': n, 'Age': a} for n, a in zip(list1, list2)] will return
[{'Name': 'ben', 'Age': 18},
{'Name': 'tim', 'Age': 12},
{'Name': 'john', 'Age': 34},
{'Name': 'wally', 'Age': 55}]

Converting a RESTful API response from list to dictionary

Currently I have a function (shown below) that makes a GET request from an API that I made myself
def get_vehicles(self):
result = "http://127.0.0.1:8000/vehicles"
response = requests.get(result)
data = response.content
data_dict = json.loads(data)
return data_dict
The data I got is in this format. Which is a list of dictionary
data_dict = [{'colour': 'Black', 'cost': 10, 'latitude': -37.806152, 'longitude': 144.95787, 'rentalStatus': 'True', 'seats': 4, 'user': None, 'vehicleBrand': 'Toyota', 'vehicleID': 1, 'vehicleModel': 'Altis'}, {'colour': 'White', 'cost': 15, 'latitude': -37.803913, 'longitude': 144.964859, 'rentalStatus': 'False', 'seats': 4, 'user': {'firstname': 'Test', 'imageName': None, 'password': 'password', 'surname': 'Ing', 'userID': 15, 'username': 'Testing'}, 'vehicleBrand': 'Honda', 'vehicleID': 3, 'vehicleModel': 'Civic'}]
Is it possible to convert it to just a dictionary? Example:
data_dict = {'colour': 'Black', 'cost': 10, 'latitude': -37.806152, 'longitude': 144.95787, 'rentalStatus': 'True', 'seats': 4, 'user': None, 'vehicleBrand': 'Toyota', 'vehicleID': 1, 'vehicleModel': 'Altis'}, {'colour': 'White', 'cost': 15, 'latitude': -37.803913, 'longitude': 144.964859, 'rentalStatus': 'False', 'seats': 4, 'user': {'firstname': 'Test', 'imageName': None, 'password': 'password', 'surname': 'Ing', 'userID': 15, 'username': 'Testing'}, 'vehicleBrand': 'Honda', 'vehicleID': 3, 'vehicleModel': 'Civic'}
No, the second result is a tuple, not a dict.
data_dict = {'colour': 'Black', 'cost': 10, 'latitude': -37.806152, 'longitude': 144.95787, 'rentalStatus': 'True', 'seats': 4, 'user': None, 'vehicleBrand': 'Toyota', 'vehicleID': 1, 'vehicleModel': 'Altis'}, {'colour': 'White', 'cost': 15, 'latitude': -37.803913, 'longitude': 144.964859, 'rentalStatus': 'False', 'seats': 4, 'user': {'firstname': 'Test', 'imageName': None, 'password': 'password', 'surname': 'Ing', 'userID': 15, 'username': 'Testing'}, 'vehicleBrand': 'Honda', 'vehicleID': 3, 'vehicleModel': 'Civic'}
print(type(data_dict))
# <class 'tuple'>
It is the same as:
data_dict = ({'colour': 'Black', 'cost': 10, 'latitude': -37.806152, 'longitude': 144.95787, 'rentalStatus': 'True', 'seats': 4, 'user': None, 'vehicleBrand': 'Toyota', 'vehicleID': 1, 'vehicleModel': 'Altis'}, {'colour': 'White', 'cost': 15, 'latitude': -37.803913, 'longitude': 144.964859, 'rentalStatus': 'False', 'seats': 4, 'user': {'firstname': 'Test', 'imageName': None, 'password': 'password', 'surname': 'Ing', 'userID': 15, 'username': 'Testing'}, 'vehicleBrand': 'Honda', 'vehicleID': 3, 'vehicleModel': 'Civic'})
That's why it is a tuple.
If you only want to merge them in a dict,it seems to be impossible because dict couldn't have the same keys.But you could merge the value as a list,like:
d = {key: list(value) for key, value in zip(data_dict[0].keys(), zip(data_dict[0].values(), data_dict[1].values()))}
print(d)
Result(Make sure they has the same length):
{
'colour': ['Black', 'White'],
'cost': [10, 15],
'latitude': [-37.806152, -37.803913],
'longitude': [144.95787, 144.964859],
'rentalStatus': ['True', 'False'],
'seats': [4, 4],
'user': [None, {
'firstname': 'Test',
'imageName': None,
'password': 'password',
'surname': 'Ing',
'userID': 15,
'username': 'Testing'
}],
'vehicleBrand': ['Toyota', 'Honda'],
'vehicleID': [1, 3],
'vehicleModel': ['Altis', 'Civic']
}
This a list of dictionaries.
Therefore you can access them using the array syntax: data_dict[0] for the first element for example.

Remove duplicates from a list of a list of unordered dictionaries

Consider the following:
[
[
{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}
],
[
{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19},
{'name': 'bob', 'score': 99}
],
[
{'name': 'bob', 'score': 99},
{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19}
],
[
{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}
]
]
Ignoring the order of the dictionaries within each list, how can duplicates be removed such that the output would be only two of the lists: one with bob and one with stu?
Output something like:
[
[
{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}
],
[
{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}
]
]
You could try something like this
dict_list = [[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}],
[{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19},
{'name': 'bob', 'score': 99}],
[{'name': 'bob', 'score': 99},
{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19}],
[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}]]
# create list of names you've seen before
name_lists = []
# create lists of unique lists
unique_lists = []
# loop over each list you have
for L in dict_list:
# get list of names
names = [i['name'] for i in L]
# check if you've seen this set of names before
if set(names) not in [set(n) for n in name_lists]:
print(names)
# save these names
name_lists.append(names)
# add this list to your list of unique names
unique_lists.append(L)
Output:
['fred', 'frank', 'bob']
['fred', 'frank', 'stu']
unique_lists
Output:
[[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}],
[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}]]
Note that this method will save only the scores for the first set of unique names and discard scores when the set of names is duplicated. If it is expected that the same names may have different scores, you may want to save every unique set of scores. In this case, you can follow the method given by PacketLoss below:
name_lists = []
unique_lists = []
for di, d in enumerate(dict_list):
# get list of name, score tuples
r = [(i['name'], i['score']) for i in d]
# sort tuples alphabetically by name
r.sort(key=lambda tup: tup[0])
# check if these names and scores have been seen before
if r not in name_lists:
name_lists.append(r)
unique_lists.append(dict_list[di])
Due to the ordering being off, a simple == will not match, we can work around this by gathering the data, sorting it as a list of tuples and checking if the match has been seen before.
data = [[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}],
[{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19},
{'name': 'bob', 'score': 99}],
[{'name': 'bob', 'score': 99},
{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19}],
[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}]]
seen = list()
result = list()
for idx, d in enumerate(data):
r = [(i['name'], i['score']) for i in d]
r.sort(key=lambda tup: tup[0])
if r not in seen:
seen.append(r)
result.append(data[idx])
With this method, we are checking that both the scores and names are a complete match, meaning if one score in a duplicate changed to 98 it would no longer be counted as a duplicate.
Output:
[[{'name': 'fred', 'score': 19}, {'name': 'frank', 'score': 100}, {'name': 'bob', 'score': 99}], [{'name': 'fred', 'score': 19}, {'name': 'frank', 'score': 100}, {'name': 'stu', 'score': 69}]]
Output with modifying scores in data:
data = [[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'bob', 'score': 99}],
[{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19},
{'name': 'bob', 'score': 99}],
[{'name': 'bob', 'score': 98},
{'name': 'frank', 'score': 100},
{'name': 'fred', 'score': 19}],
[{'name': 'fred', 'score': 19},
{'name': 'frank', 'score': 100},
{'name': 'stu', 'score': 69}]]
[[{'name': 'fred', 'score': 19}, {'name': 'frank', 'score': 100}, {'name': 'bob', 'score': 99}], [{'name': 'bob', 'score': 98}, {'name': 'frank', 'score': 100}, {'name': 'fred', 'score': 19}], [{'name': 'fred', 'score': 19}, {'name': 'frank', 'score': 100}, {'name': 'stu', 'score': 69}]]

How to reorder a list in Python based on its content

I have a list of dictionaries in python like this;
l = [{'name': 'John', 'age': 23},
{'name': 'Steve', 'age': 35},
{'name': 'Helen'},
{'name': 'George'},
{'name': 'Jessica', 'age': 23}]
What I am trying to achieve here is reorder the elements of l in such a way that each entry containing the key age move to the end of the list like this;
End result:
l = [{'name': 'Helen'},
{'name': 'George'},
{'name': 'Jessica', 'age': 23},
{'name': 'John', 'age': 23},
{'name': 'Steve', 'age': 35}]
How can I do this?
You can sort the list:
l.sort(key=lambda d: 'age' in d)
The key returns either True or False, based on the presence of the 'age' key; True is sorted after False. Python's sort is stable, leaving the rest of the relative ordering intact.
Demo:
>>> from pprint import pprint
>>> l = [{'name': 'John', 'age': 23},
... {'name': 'Steve', 'age': 35},
... {'name': 'Helen'},
... {'name': 'George'},
... {'name': 'Jessica', 'age': 23}]
>>> l.sort(key=lambda d: 'age' in d)
>>> pprint(l)
[{'name': 'Helen'},
{'name': 'George'},
{'age': 23, 'name': 'John'},
{'age': 35, 'name': 'Steve'},
{'age': 23, 'name': 'Jessica'}]
If you also wanted to sort by age, then retrieve the age value and return a suitable stable sentinel for those entries that do not have an age, but which will be sorted first. float('-inf') will always be sorted before any other number, for example:
l.sort(key=lambda d: d.get('age', float('-inf')))
Again, entries without an age are left in their original relative order:
>>> l.sort(key=lambda d: d.get('age', float('-inf')))
>>> pprint(l)
[{'name': 'Helen'},
{'name': 'George'},
{'age': 23, 'name': 'John'},
{'age': 23, 'name': 'Jessica'},
{'age': 35, 'name': 'Steve'}]

sort a list of dicts by x then by y

I want to sort this info(name, points, and time):
list = [
{'name':'JOHN', 'points' : 30, 'time' : '0:02:2'},
{'name':'KARL','points':50,'time': '0:03:00'}
]
so, what I want is the list sorted first by points made, then by time played (in my example, matt go first because of his less time. any help?
I'm trying with this:
import operator
list.sort(key=operator.itemgetter('points', 'time'))
but got a TypeError: list indices must be integers, not str.
Your example works for me. I would advise you not to use list as a variable name, since it is a builtin type.
You could try doing something like this also:
list.sort(key=lambda item: (item['points'], item['time']))
edit:
example list:
>>> a = [
... {'name':'JOHN', 'points' : 30, 'time' : '0:02:20'},
... {'name':'LEO', 'points' : 30, 'time': '0:04:20'},
... {'name':'KARL','points':50,'time': '0:03:00'},
... {'name':'MARK','points':50,'time': '0:02:00'},
... ]
descending 'points':
using sort() for inplace sorting:
>>> a.sort(key=lambda x: (-x['points'],x['time']))
>>> pprint.pprint(a)
[{'name': 'MARK', 'points': 50, 'time': '0:02:00'},
{'name': 'KARL', 'points': 50, 'time': '0:03:00'},
{'name': 'JOHN', 'points': 30, 'time': '0:02:20'},
{'name': 'LEO', 'points': 30, 'time': '0:04:20'}]
>>>
using sorted to return a sorted list:
>>> pprint.pprint(sorted(a, key=lambda x: (-x['points'],x['time'])))
[{'name': 'MARK', 'points': 50, 'time': '0:02:00'},
{'name': 'KARL', 'points': 50, 'time': '0:03:00'},
{'name': 'JOHN', 'points': 30, 'time': '0:02:20'},
{'name': 'LEO', 'points': 30, 'time': '0:04:20'}]
>>>
ascending 'points':
>>> a.sort(key=lambda x: (x['points'],x['time']))
>>> import pprint
>>> pprint.pprint(a)
[{'name': 'JOHN', 'points': 30, 'time': '0:02:20'},
{'name': 'LEO', 'points': 30, 'time': '0:04:20'},
{'name': 'MARK', 'points': 50, 'time': '0:02:00'},
{'name': 'KARL', 'points': 50, 'time': '0:03:00'}]
>>>
itemgetter will throw this error up to Python2.4
If you are stuck on 2.4, you will need to use the lambda
my_list.sort(key=lambda x: (x['points'], x['time']))
It would be preferable to upgrade to a newer Python if possible

Categories