By Amelia Brewin
Sorting objects is a requirement that comes up from time to time. It can be as simple as ordering by a simple number field (such as Opportunities priority) or more complex ordering involving multiple fields.
Luckily for us Salesforce has the Comparable Interface. With a handy wrapper class that implements Comparable and takes our object in the constructor we can define our own ordering.
This list is being displayed in a Visualforce table however you could easily adapt it to update a field or other requirements. In this example I am sorting a List of Contacts to determine the order to call them, my sorting criteria are:
- Do Not Call is not selected.
- The contact has a phone number in either Phone, Mobile Phone or Other Phone fields.
- Those in the Finance department are higher.
- Created date is higher.
Step 1:
Create the controller with the wrapper class, the wrapper constructor will take a contact object. In the controller constructor we will query the contacts and create a list of contact wrappers that will be sorted and passed to the Visualforce page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class ContactSorter_Cont { public List<Contact> contacts {get;set;} public List<ContactWrapper> contactWrappers {get;set;} public ContactSorter_Cont () { contacts = new List<Contact>([SELECT Id, Name, DoNotCall, Department, Phone, OtherPhone, MobilePhone, CreatedDate FROM Contact]); contactWrappers = new List<ContactWrapper>(); for(Contact c : contacts){ contactWrappers.add(new ContactWrapper(c)); } contactWrappers.sort(); } public class ContactWrapper implements Comparable{ public Contact contact {get;set;} public ContactWrapper(Contact c){ contact = c; } public Integer compareTo(Object compareTo){ ContactWrapper otherContact = (ContactWrapper)compareTo; } } |
Step 2
The contact wrapper needs variables of all the values we will be using to sort. In the constructor we will assign the values of the from the contact to these variables. In our case we also want a boolean variable this will represent contacts that have a number in at least one of the phone number fields.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class ContactWrapper implements Comparable{ public Contact contact {get;set;} public String name {get;set;} public DateTime createdDate {get;set;} public Boolean doNotCall {get;set;} public String department {get;set;} public String phone {get;set;} public String mobilePhone {get;set;} public String otherPhone {get;set;} public Boolean phonePresent {get;set;} public ContactWrapper(Contact c){ contact = c; name = c.Name; createdDate = c.createdDate; doNotCall = c.DoNotCall; department = c.Department; phone = c.Phone; mobilePhone = c.MobilePhone; otherPhone = c.OtherPhone; if(String.isNotEmpty(phone) || String.isNotEmpty(mobilePhone) || String.isNotEmpty(otherPhone)){ phonePresent = true; } else { phonePresent = false; } } } |
Step 3:
Now is time to set our sorting requirements. To do this we override the compareTo method from the Comparble interface. This needs to return a number:
- 0 if the records are equal.
- 1 if the record is greater than the comparison record.
- -1 if record is less than the comparison record.
Booleans
Our first criteria is Do Not Call, if both the current record and the one being compared to are the same we return 0, if the current record is true (they don’t want to be called) and the compared to is false (they do want to be called) we want the current record to move above so return -1. And vice versa if the current record is false (they want to be called) and the compared to is true (they don’t want to be called) we want the current record to move below so return 1.
1 2 3 4 5 6 7 8 9 10 |
public Integer compareTo(Object compareTo){ ContactWrapper otherContact = (ContactWrapper)compareTo; if(doNotCall == otherContact.doNotCall){ return 0; } else if(doNotCall && !otherContact.doNotCall){ return 1; } else { return -1; } } |
The variable phonePresent is also a boolean that we have created in the constructor of contact wrapper. It represents a contact that has a number in one of the phone fields.
1 2 3 4 5 |
if(String.isNotEmpty(phone) || String.isNotEmpty(mobilePhone) || String.isNotEmpty(otherPhone)){ phonePresent = true; } else { phonePresent = false; } |
So we want those contacts where do not call is the same to then use the secondary sorting criteria of phonePresent, therefore where we previously returned 0 add in the condition about phonePresent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public Integer compareTo(Object compareTo){ ContactWrapper otherContact = (ContactWrapper)compareTo; if(doNotCall == otherContact.doNotCall){ if(phonePresent == otherContact.phonePresent){ return 0; } else if(phonePresent && !otherContact.phonePresent){ return 1; } else { return -1; } } else if(doNotCall && !otherContact.doNotCall){ return 1; } else { return -1; } } |
Strings
If you want simply to order alphabetically you can use the operators:
1 2 3 4 5 6 7 |
if(department == otherContact.department){ return 0; } else if(department < otherContact.department){ return -1; } else { return 1; } |
Or you can give specific strings, in our case I want those in the Finance department to be at the top of the list:
1 2 3 4 5 6 7 |
if(department == otherContact.department){ return 0; } else if(department == 'Finance' && otherContact.department != 'Finance'){ return -1; } else { return 1; } |
Dates and Numbers
Dates and numbers can are very easy to order using the standard comparison operators. We just need to check if they’re equal, greater than or less than.
1 2 3 4 5 6 7 |
if(createdDate == otherContact.createdDate){ return 0; } else if(createdDate > otherContact.createdDate){ return -1; } else { return 1; } |
The completed wrapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
public class ContactWrapper implements Comparable{ public Contact contact {get;set;} public String name {get;set;} public DateTime createdDate {get;set;} public Boolean doNotCall {get;set;} public String department {get;set;} public String phone {get;set;} public String mobilePhone {get;set;} public String otherPhone {get;set;} public Boolean phonePresent {get;set;} public ContactWrapper(Contact c){ contact = c; name = c.Name; createdDate = c.createdDate; doNotCall = c.DoNotCall; department = c.Department; phone = c.Phone; mobilePhone = c.MobilePhone; otherPhone = c.OtherPhone; if(String.isNotEmpty(phone) || String.isNotEmpty(mobilePhone) || String.isNotEmpty(otherPhone)){ phonePresent = true; } else { phonePresent = false; } } public Integer compareTo(Object compareTo){ ContactWrapper otherContact = (ContactWrapper)compareTo; if(doNotCall == otherContact.doNotCall){ if(phonePresent == otherContact.phonePresent){ if(department == otherContact.department){ if(createdDate == otherContact.createdDate){ return 0; } else if(createdDate > otherContact.createdDate){ return -1; } else { return 1; } } else if(department == 'Finance' && otherContact.department != 'Finance'){ return -1; } else { return 1; } } else if(phonePresent && !otherContact.phonePresent){ return -1; } else { return 1; } } else if(doNotCall && !otherContact.doNotCall){ return 1; } else { return -1; } } } } |
Step 4:
Once we have the list of our ContactWrappers all we have to do to sort is call .sort() on the list. This rearranges all the records in the list and we can use however we need. We could:
Iterate over them and assigning the index to a variable such as priority eg:
1 2 3 |
for(Integer i=0; i<contactWrappers.size(); i++){ contactWrappers[i].contact.Priority__c = i+1; } |
Display them in a Visualforce table in the sorted order eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<apex:page controller="ContactSorter_Cont" > <apex:sectionheader title="Contacts" subtitle="Calling List" /> <apex:pageBlock > <apex:pageBlockTable value="{!contactWrappers}" var="cw"> <apex:column value="{!cw.contact.Name}"/> <apex:column value="{!cw.contact.Department}"/> <apex:column value="{!cw.contact.DoNotCall}"/> <apex:column value="{!cw.contact.Phone}"/> <apex:column value="{!cw.contact.MobilePhone}"/> <apex:column value="{!cw.contact.OtherPhone}"/> <apex:column value="{!cw.contact.CreatedDate}"/> </apex:pageBlockTable> </apex:pageBlock> </apex:page> |
The Result:
In the screenshot below, the Finance contacts are highlighted. The first group have a phone number, and the Do Not Call flag is not selected, so these are listed first. Notice how the most recently created contact is listed first. Contacts from other departments are then listed, that have a phone number. Then comes another Finance contact, as even though it doesn’t have a phone number, the contacts in the Finance department are ordered higher than other departments if all else is the same. The last Finance contact is second last, as the Do Not Call flag is ticked.
Complete Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
public class ContactSorter_Cont { public List<Contact> contacts {get;set;} public List<ContactWrapper> contactWrappers {get;set;} public ContactSorter_Cont () { contacts = new List<Contact>([SELECT Id, Name, DoNotCall, Department, Phone, OtherPhone, MobilePhone, CreatedDate FROM Contact]); contactWrappers = new List<ContactWrapper>(); for(Contact c : contacts){ contactWrappers.add(new ContactWrapper(c)); } contactWrappers.sort(); } public class ContactWrapper implements Comparable{ public Contact contact {get;set;} public String name {get;set;} public DateTime createdDate {get;set;} public Boolean doNotCall {get;set;} public String department {get;set;} public String phone {get;set;} public String mobilePhone {get;set;} public String otherPhone {get;set;} public Boolean phonePresent {get;set;} public ContactWrapper(Contact c){ contact = c; name = c.Name; createdDate = c.createdDate; doNotCall = c.DoNotCall; department = c.Department; phone = c.Phone; mobilePhone = c.MobilePhone; otherPhone = c.OtherPhone; if(String.isNotEmpty(phone) || String.isNotEmpty(mobilePhone) || String.isNotEmpty(otherPhone)){ phonePresent = true; } else { phonePresent = false; } } public Integer compareTo(Object compareTo){ ContactWrapper otherContact = (ContactWrapper)compareTo; if(doNotCall == otherContact.doNotCall){ if(phonePresent == otherContact.phonePresent){ if(department == otherContact.department){ if(createdDate == otherContact.createdDate){ return 0; } else if(createdDate < otherContact.createdDate){ return -1; } else { return 1; } } else if(department == 'Finance'){ return -1; } else { return 1; } } else if(phonePresent && otherContact.phonePresent){ return 1; } else { return -1; } } else if(doNotCall && !otherContact.doNotCall){ return 1; } else { return -1; } } } } |
Another way to sort on multiple fields in APex is to write your data to a table used to store data temporarily and then simply retrieve the same data again in a list using SOQL and use the ‘order by’ clause to sort the way you want.
The temporary data in the table can then be deleted using the same list you retrieved. You will still have the list to work with.