Reputation: 1573
In my program I want to order all the contacts by their name, last name first, then by first name.
I've got the code which does that for me but it doesn't do it exactly as I want it to.
For example, if I have a list of names ordered as the current code would do, it would go like this:
Luke
Riyaan
Amanda Benson
As you can see, the code still takes None
as a value to sort on, what I want is this:
Amanda Benson
Luke
Riyaan
So basically, if the last name returns None
then I want the program to order the first name with the same precedence of an object that does have a last name.
Here's the code that I'm currently using to sort the names:
import operator
...
addressBook = AddressBook()
addressBook.contactsList
addressBook.contactsList.sort(key = operator.attrgetter("lastName", "firstName"))
Upvotes: 0
Views: 121
Reputation: 122106
The best way to do this is to implement the rich comparison magic methods in your (I guess) Contact
class:
class Contact(object):
def __init__(self, first_name, last_name=""):
self.first_name = first_name
self.last_name = last_name
def __repr__(self): # to make the demo clearer!
if not self.last_name:
return str(self.first_name)
return "{0.first_name} {0.last_name}".format(self)
def __eq__(self, other):
return (self.first_name == other.first_name and
self.last_name == other.last_name)
def __lt__(self, other):
if self.last_name and other.last_name:
if self.last_name == other.last_name:
return self.first_name < other.first_name
return self.last_name < other.last_name
else:
if other.last_name:
return self.first_name < other.last_name
return self.first_name < other.first_name
Now you can just use sorted
and list.sort
without arguments:
>>> contacts_list = [Contact("Luke"), Contact("Riyaan"), Contact("Amanda", "Benson")]
>>> sorted(contacts_list)
[Amanda Benson, Luke, Riyaan]
Upvotes: 0
Reputation: 1123850
You'll need a custom sort function then that returns your first name as the first value in a tuple, when the last name is missing:
def name_sort(contact):
if contact.lastName:
return contact.lastName, contact.firstName
return contact.firstName, ''
addressBook.contactsList.sort(key=name_sort)
You can use a lambda
and conditional expression to fit that into the sort()
call:
addressBook.contactsList.sort(key=lambda c: (c.lastName, c.firstName) if c.lastName else (c.firstName, ''))
I produce a 2-value tuple here for both cases, but a one-element tuple for the no-last-name case should suffice too.
If this is the only sort order you are interested in, you may want to look in providing the rich comparison functions so you can sort your objects without a key; the objects themselves are then compared and provide the sort order.
You don't have to implement all rich-comparison methods to do so; all you need is one of them, plus __eq__
(equality testing), and the @functools.total_ordering()
class decorator:
from functools import total_ordering
@total_ordering
class Contact(object):
# ...
def __eq__(self, other):
if self.lastName != other.lastName:
return False
return self.firstName == other.firstName
def __lt__(self, other):
if not self.lastName:
if not other.lastName:
return self.firstName < other.firstName
return self.firstName < other.lastName
return (self.lastName, self.firstName) < (other.lastName, other.firstName)
Upvotes: 3