In previous post i.e. “Apex Summer’12 Comparable interface Sorting Examples : Part 1”, we saw how easily we can sort a UDT(User Defined Type) on various attributes like Date, Datetime, String and Numbers.
This post will demo how we can get the ability to sort on
Multiple attributes
Both ASC and DESC sorting orders
All using the single Comparable interface. I wish Apex had “Comparators” like java, for more details on this gap, please check the “Summer’12 sorting review enhancements review” post.
Sample Apex Class – Employee
The same Employee class used as example in previous post will be enhanced here. Following is the initial code
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
}
}
Magic of “static”
static context i.e. variables are not like usual static in other languages like Java. In Java a static variable retains the value till the JVM(Java Virtual Machine) or application is running. But in Apex its having a totally different meaning because of multi-tenant nature of force.com platform, they can’t keep your statics available forever. If you are curious about how static works in Apex, I recommend reading this post first
Deep Dive – Static Context in Visualforce Apex Controllers !
The same special nature of static is used to support multi-attribute/order sorting via Comparable in this post.
Enhanced Employee Class
Here is the class code, I tried adding decent comments to make it more human readable and understandable
global class Employee implements Comparable {
// Which field should be considered for sorting
public enum SortField {
Name, Age, DOJ, CheckInTime
}
// Sorting direction ASCENDING or DESCENDING
public enum SortDirection {
ASCENDING, DESCENDING
}
// default sorting would be ascending
public static SortDirection SORT_DIR = SortDirection.ASCENDING;
// default sorting would be on name
public static SortField SORT_FIELD = SortField.Name;
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;
}
/*
Comparable.compareTo() implementation
*/
global Integer compareTo(Object other) {
if (SORT_FIELD == SortField.Name) {
return compareToName(other);
} else if (SORT_FIELD == SortField.Age) {
return compareToAge(other);
} else if (SORT_FIELD == SortField.DOJ) {
return compareToDoj(other);
} else if (SORT_FIELD == SortField.CheckInTime) {
return compareToCheckInTime(other);
}
// this shouldn't be the case, add your error handling
// here as required
return 0;
}
// compares CheckInTimeField
Integer compareToCheckInTime(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
if (SORT_DIR == SortDirection.ASCENDING)
return (this.checkInTime.getTime() - otherCheckInTime.getTime()).intValue();
else
return (otherCheckInTime.getTime() - this.checkInTime.getTime()).intValue();
}
// Compares DOJ field
Integer compareToDoj(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();
if (SORT_DIR == SortDirection.ASCENDING)
return otherDOJ.daysBetween(this.doj);
else
return this.doj.daysBetween(otherDOJ);
}
// Compares NAME field
Integer compareToName(Object other) {
String otherName = other != null ? ((Employee)other).name : '';
if (SORT_DIR == SortDirection.ASCENDING)
return this.name.compareTo(otherName);
else
return otherName.compareTo(this.name);
}
// Compares AGE field
Integer compareToAge(Object other) {
Integer otherAge = other != null ? ((Employee)other).age : 0;
if (SORT_DIR == SortDirection.ASCENDING)
return this.age - otherAge;
else
return otherAge - this.age;
}
}
Testing the sorting
Execute this code snippet in anonymous block, and watch debug logs to trap the results. I have added how to sort on Name and Age fields in ASC and DESC order, you can sort of remaining two in similar fashion.
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))
};
// sort by default name and asc order
employees.sort();
System.debug(employees);
// Reverse the sorting order to be desc
Employee.SORT_DIR = Employee.SortDirection.DESCENDING;
employees.sort();
System.debug(employees);
// sort order on Age in ASC order
Employee.SORT_DIR = Employee.SortDirection.ASCENDING;
Employee.SORT_FIELD = Employee.SortField.Age;
employees.sort();
System.debug(employees);
Related reading
Your Thoughts
Looking forward for the same.
Comments (4)
Anonymoussays:
May 24, 2012 at 12:25 pmGreat display of the Comparable and Sorting in Summer 12. Loved it. Thanks for the code snippets.
Anonymoussays:
May 24, 2012 at 12:49 pmI'm glad you liked Kartik 🙂
Anonymoussays:
February 4, 2013 at 12:07 pmHi Abhinav,I tried this for my pageblocktable and its working great .. if all the columns have data…..does it work ? suppose in my table one column “Name” is there and it contains few rows as null… then its throwing Exception …
Anonymoussays:
February 4, 2013 at 12:32 pmVeeru, I haven't added NULL checks in the code snippets for keeping the snippet code more readable and simple for the purpose. You can add NULL check to the required compare method for the same.