I currently have a list of lists, something of this kind:
[['NJ', '10', '2000', '500', '20', '02-03-19', '15:20'],
['NJ', '15', '1500', '600', '20', '02-03-19', '15:30'],
['NYC', '25', '1500', '500', '10', '02-03-19', '15:30'],
['NYC', '15', '1200', '700', '1', '02-03-19', '15:35']]
And I need to sort them with several elements in mind, for example, let's say, in terms of index numbers, 0 is area, 1 is weight, 2 is distance, 3 is height, 4 is autonomy, 5 is date and 6 is a timestamp. I am already sorting all of the elements using this:
list.sort(key=itemgetter(0, time_sorter, 4, 3))
Where time_sorter() is a function I had previously built that sorts lists based on element time stamps. My problem is that I need to sort this list with "area" in mind. For example, say I need to sort the list with all those elements in mind, as I am, but I would also like to sort it, simultaneously, in way that the elements which have "NYC" on their 0 index position are placed first, how do I go about this?
Bonus question would be: If I have multiple parameters in itemgetter()
or the sort "key" parameter itself, and I want the sorting to be reverse but for only one of those arguments, how do I go about that?
Is my best option really to separate all these sorts into several functions and call those in the sort key?
Thanks in advance!
Make a function that returns the sort key for each item. The function will be called multiple times, each time with a single item as parameter, and you can call other functions inside it if you want.
def _key(row):
return row[0], time_sorter(row), row[4], row[3]
Whatever you return will be used to sort the items:
mylist.sort(key=_key)
itemgetter
is just a simple function factory that creates a function to get items. If you need anything more complex you should make your own function
With numbers, you can reverse the sorting on some of the keys but not others by selectively using a minus sign, eg vectors.sort(key=lambda v: (vx, -vy))
sorts a list of vectors
first by their x
component in ascending order, then by y
component in descending order as a tie-breaker. Unfortunately, there is no equivalent way to do this with strings.
A good option is to write a class to represent your data as objects instead of lists, and define a custom ordering on your class. The functools.total_ordering decorator makes this easier, so you only have to define __eq__
and __lt__
instead of all six comparison methods:
from functools import total_ordering
@total_ordering
class PlaceAndTime:
def __init__(self, area, weight, distance, height, autonomy, date, time):
self.area = area
self.weight = weight
self.distance = distance
self.height = height
self.autonomy = autonomy
self.date = date
self.time = time
def __eq__(self, other):
return self.__dict__ == other.__dict__
def __lt__(self, other):
# test whether self < other
if not isinstance(other, PlaceAndTime):
raise ValueError('Not comparable')
elif self.area != other.area:
# sort by area in reverse order using > instead of <
return self.area > other.area
elif self.date != other.date:
return self.date < other.date
elif self.time != other.time:
return self.time < other.time
elif self.autonomy != other.autonomy:
# sort by autonomy in reverse order using > instead of <
return self.autonomy > other.autonomy
else:
return self.height < other.height
This way you can sort a list of objects simply using objects.sort()
without providing a key. If you specifically want to sort one area like 'NYC'
to the start of the list, you can still use a custom key:
objects.sort(key=lambda obj: (obj.area != 'NYC', obj))
This works because when obj.area == 'NYC'
the comparison will be False
, otherwise it will be True
, so the comparison False < True
means 'NYC'
will appear before other areas.
Writing a class likely has other benefits too, eg it lets you write obj.time
instead of the less-descriptive lst[6]
, and there may be other methods you can write which simplify other parts of your code.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.