I am happy about the native sorting support in Apex for UDT(User Defined Types), I was missing this a lot since I am java developer who is used to of Comparable and Comparator based sorting.
Another reason of drafting this post, is because I didn’t liked the sorting example available in apex developer guide, this example is complex way to achieve the desired.
Sample APEX Class
Following apex class with following 4 different type of attributes is used for illustration
Name : Type = String
Age : Type = Integer
DOJ : Type = Date
checkInTime : Type = Datetime
global class Employee implements Comparable {
public String name;
public Integer age;
public Date doj;
public Datetime checkInTime;
public Employee (String n, Integer a, Date d, DateTime c) {
this.name = n;
this.age = a;
this.doj = d;
this.checkInTime = c;
}
global Integer compareTo(Object other) {
// will be shown in code snippets below
}
}
Sample Test Fixture
Once the comparable contract i.e. compateTo() method is added, you can test the functionality by executing the code below in anonymous block
Employee [] employees = new Employee[] {
new Employee('Abhinav', 30, Date.newInstance(2011, 06, 16), System.now().addHours(1)),
new Employee('Vijay', 20, Date.newInstance(2010, 01, 11), System.now().addHours(2)),
new Employee('John', 50, Date.newInstance(2007, 01, 1), System.now().addHours(3)),
new Employee('Bill', 40, Date.newInstance(2000, 04, 3), System.now().addHours(4))
};
// this will sort on the basis of Comparable
employees.sort();
// print to see the results
System.debug(employees);
Sorting by Age (Integer)
Sorting on number attributes is simplest, you can just subtract the numbers to return the negative, zero or positive value. Here is how Employee class will be naturally sortable on age
// sort on age
global Integer compareTo(Object other) {
Integer otherAge = other != null ? ((Employee)other).age : 0;
// subtraction between this and other age will do the required
return this.age - otherAge;
}
on executing the above test fixture this prints
Employee:[age=20, checkInTime=2012-05-23 14:26:22, doj=2010-01-11 00:00:00, name=Vijay],
Employee:[age=30, checkInTime=2012-05-23 13:26:22, doj=2011-06-16 00:00:00, name=Abhinav],
Employee:[age=40, checkInTime=2012-05-23 16:26:22, doj=2000-04-03 00:00:00, name=Bill]
Employee:[age=50, checkInTime=2012-05-23 15:26:22, doj=2007-01-01 00:00:00, name=John],
Sorting by Name (String)
Sorting strings is simple because of presence of String.compareTo(String) method. What this means is String implements Comparable as well, so we can use call the same method to do the heavy lifting. Here is the Employee class sortable on name attribute
// sort on name
global Integer compareTo(Object other) {
String otherName = other != null ? ((Employee)other).name : '';
// use string class compareTo()
return this.name.compareTo(otherName);
}
Here are the results
Employee:[age=30, checkInTime=2012-05-23 13:33:39, doj=2011-06-16 00:00:00, name=Abhinav],
Employee:[age=40, checkInTime=2012-05-23 16:33:39, doj=2000-04-03 00:00:00, name=Bill],
Employee:[age=50, checkInTime=2012-05-23 15:33:39, doj=2007-01-01 00:00:00, name=John],
Employee:[age=20, checkInTime=2012-05-23 14:33:39, doj=2010-01-11 00:00:00, name=Vijay]
Sorting on DOJ (Date)
With dates we can use the daysBetween() method to do the heavy lifting, as indicated below:
// sort on date
global Integer compareTo(Object other) {
// assuming if their is no date, it would be TODAY
// this might not be applicable for your biz logic
// so please take care of that
Date otherDOJ = other != null ? ((Employee)other).doj : System.today();
return otherDOJ.daysBetween(this.doj);
}
here are the results
Employee:[age=40, checkInTime=2012-05-23 16:26:22, doj=2000-04-03 00:00:00, name=Bill],
Employee:[age=50, checkInTime=2012-05-23 15:26:22, doj=2007-01-01 00:00:00, name=John],
Employee:[age=20, checkInTime=2012-05-23 14:26:22, doj=2010-01-11 00:00:00, name=Vijay],
Employee:[age=30, checkInTime=2012-05-23 13:26:22, doj=2011-06-16 00:00:00, name=Abhinav],
Sorting on checkInTime(DateTime)
Again with Datetime, we can use time past in millis to make the sorting easy. Here is the sample
global Integer compareTo(Object other) {
// assuming if their is no datetime, it would be NOW
// this might not be applicable for your biz logic
// so please take care of that
DateTime otherCheckInTime = other != null ? ((Employee)other).checkInTime : System.now();
// use Datetime.getTime() to do get the numeric time in millis
return (this.checkInTime.getTime() - otherCheckInTime.getTime()).intValue();
}
here are the results
Employee:[age=30, checkInTime=2012-05-23 13:26:22, doj=2011-06-16 00:00:00, name=Abhinav],
Employee:[age=20, checkInTime=2012-05-23 14:26:22, doj=2010-01-11 00:00:00, name=Vijay],
Employee:[age=50, checkInTime=2012-05-23 15:26:22, doj=2007-01-01 00:00:00, name=John],
Employee:[age=40, checkInTime=2012-05-23 16:26:22, doj=2000-04-03 00:00:00, name=Bill])
Sorting in DESCENDING order
All the above sort samples, do the sorting in ASCENDING order. Reversing the order is very easy, one can just swap the this and other attributes in compareTo() method, here are the swapped line samples
//Name
return otherName.compareTo(this.name);
// CheckinTime
return (otherCheckInTime.getTime() - this.checkInTime.getTime()).intValue();
//DOJ
return this.doj.daysBetween(otherDOJ);
//Age
return otherAge - this.age;
Related reading
Your thoughts
Looking forward for the same !
Comments (4)
Anonymoussays:
November 16, 2012 at 6:48 pmYour DateTime sort (checkInTime example) won't work if the difference between DateTime values is about 25 days or more. The difference in millis exceeds the maximum value of an integer so the conversion to integer with intValue() flips the sign.
Anonymoussays:
November 17, 2012 at 5:15 amGood catch xja, I will look into it, what solution do you suggest ?
Anonymoussays:
February 26, 2013 at 7:08 pmHi Abhinav..Did you get a chance to solve the 25 days issue that xja mentioned? Please let me know its urgent..
Anonymoussays:
February 27, 2013 at 3:54 amSorry Kris and Xja, I was too much loaded with work and this TODO missed out. One quick thought would be to do it like thislong diff = this.checkInTime.getTime() – otherCheckInTime.getTime();return diff > 0 ? 1 : (diff < 0) ? -1 : 0;I am short on time today to test it, but hopefully this should work. Please let me know how it goes.