1

Sorting in Apex and JavaScript

In this short post we’ll look at techniques for sorting Apex lists and JavaScript arrays. It may seem like a trivial topics but there are a few gotchas and tips that you may find useful. We’ll cover the base sorting methods for these two language and present how you can write custom sort logic.

Sorting in Apex

The first choice when it comes to sorting things in Apex is generally to write a SOQL query with an ORDER BY clause. However, sometimes you need to sort records without fetching them from the database or even sort objects that are not records. In those cases, you use the List.sort method like this:

List<String> colors = new List<String>{ 'Yellow', 'Red', 'Green' };
colors.sort(); // Sorts list of string alphabetically
System.assertEquals('Green', colors.get(0));
System.assertEquals('Red', colors.get(1));
System.assertEquals('Yellow', colors.get(2));

The List.sort method is easy to use but let’s take a closer look at how it works and how you can sort with custom ordering logic.

Working with the Comparable interface

List.sort works with list elements that implement the Comparable interface. The interface specifies a single method:

public Integer compareTo(Object objectToCompareTo)

CompareTo returns an integer with the following values:

  1. 0 if this instance and objectToCompareTo are equal
  2. > 0 if this instance is greater than objectToCompareTo
  3. < 0 if this instance is less than objectToCompareTo

The sorting algorithm form List.sort can sort any mix of object with various types as long as they all implements Comparable. For instance, you could perfectly write something like this:

List<Object> myList = new List<Object>{ 4, true, 'World', 'Hello', 1, false };
myList.sort();
System.debug(myList); // (false, true, 1, 4, Hello, World)

As a word of waring, it’s safe to have objects that don’t implement Comparable in a list but if you call the sort method on that list, you’ll get an exception:

System.ListException: One or more of the items in this list is not Comparable.

The sort method documentation provides an example implementation for sorting a custom class so we will not dive in details on this topic but what about sorting records (SObject class)?

Sorting lists of sObjects

SObject implements the Comparable interface and instances of that class have a predictable sort order. However, you may need to implement a custom sort order in some cases and this is where things get more complex. An important constraint of SObject is that it is not extendable so you can’t simply overwrite the default compareTo method. For example, the following code is invalid because Account can’t be extended:

public class InvalidSortableAccount extends Account {
    public override Integer compareTo(Object otherObject) {
     // My custom ordering logic
    }
}

To compensate for that, you have to work with a wrapper class around the SObject that you want to sort. Let’s imagine that we want to sort a list of accounts based on the shipping country field. We would write the following class:

public class SortableAccount implements Comparable {
  private final Account account;
  public SortableAccount(Account account) {
   this.account = account;
  }
  /**
   * Sort accounts based on ShippingCountry
   */
  public Integer compareTo(Object otherObject) {
   // For additional type safety, check if otherObject is a SortableAccount
   // if not, throw a SortException
   if (!(otherObject instanceof SortableAccount)) {
     throw new SortException('Can\'t sort with incompatible type');
   }
   // Cast otherObject to SortableAccount and compare it
   SortableAccount other = (SortableAccount) otherObject;
   if (this.account.ShippingCountry < other.account.ShippingCountry) {
     return -1;
   }
   if (this.account.ShippingCountry > other.account.ShippingCountry) {
     return 1;
   }
   return 0;
  }
  
  public class SortException extends Exception {}
}

We can then sort an account list with our custom ordering logic with these steps:

  1. convert our List<Account> into a List<SortableAccount>
  2. call the sort method on List<SortableAccount>
  3. convert our List<SortableAccount> back to List<Account>

This is not practical as it would take as many lines of code as the implementation of SortableAccount and those extra lines wouldn’t be reusable. Fortunately, there’s something that you can do to reduce boilerplate code: simply add the following static method to SortableAccount.

public static void sort(List<Account> accounts) {
   // Convert List<Account> into List<SortableAccount>
   List<SortableAccount> sortableAccounts = new List<SortableAccount>();
   for (Account acc : accounts) {
     sortableAccounts.add(new SortableAccount(acc));
   }
   // Sort accounts (this uses SortableAccount.compareTo)
   sortableAccounts.sort();
   // Overwrite the account list provided in the input parameter
   // with the sorted list. Doing this avoids a return statement
   // and is less verbose for the method user.
   for (Integer i = 0; i < accounts.size(); i++) {
     accounts[i] = sortableAccounts[i].account;
   }
}

With this SortableAccount.sort method, all it takes to sort a list of accounts (List) by shipping country is a single line

SortableAccount.sort(accounts);

Check out this GitHub repository for the complete implementation along with the related test class.

Sorting lists with reusable comparators

While the default List.sort method is convenient, it has two important limitations:

  1. the ordering logic is directly tied to the Comparable object that is being sorted
  2. the sort method lacks the ability to sort with different strategies and parameters

For example, the Java language (which is close to Apex) goes beyond the basic sort method implementation and exposes a convenient Arrays.sort(T[], Comparator) method where T is the type being sorted and Comparator an interface that specifies a compare method that works like Comparable.compareTo:

Integer compare(Object o1, Object o2);

With this approach, you can sort lists with different comparators and even pass parameters to comparators. You benefitfrom the fact that the ordering logic is decoupled from the objects that you sort. Here’s an example of how this can be applied to Apex with a custom ListUtils class and Comparator interface that let you sort lists with reusable comparators:

// Sort a list of accounts alphabetically based on shipping country
ListUtils.sort(accounts, new SObjectStringFieldComparator('ShippingCountry'));
// Sort a list of accounts alphabetically based on industry
ListUtils.sort(accounts, new SObjectStringFieldComparator('Industry'));
// Sort a list of accounts based on rating values
// as defined in the rating picklist order (non-alphabetical sort: )
ListUtils.sort(accounts, new AccountRatingComparator());

Checkout the GitHub repository for the full implementation and related test classes.

Sorting in JavaScript

Sorting in JavaScript works similarly to Apex and custom sorting logic is even easier to implement because of the language’s loose typing. All it takes is a custom compare function that returns an integer (just like the Apex Comparable.compareTo function) and a call to the Array.sort(comparatorFunction) method.

With this approach, you can easily sort anything with reusable compare functions. Here’s an example of how you could sort a list of size strings (Small, Medium and Large) from smallest to largest:

// Compare function for size strings
// Orders sizes as: Small < Medium < Large
const sizeCompareFunction = (a, b) => {
  const valA = sizeToNumber(a);
  const valB = sizeToNumber(b);
  if (valA < valB) {
   return -1;
  }
  if (valA > valB) {
   return 1;
  }
  return 0;
};
// Converts size string into a number to ease sorting
// Small = 0, Medium = 1, Large = 2
const sizeToNumber = (sizeString) => {
  switch (sizeString) {
   case 'Small':
   return 0;
   case 'Medium':
   return 1;
   case 'Large':
   return 2;
   default:
   throw new Error(`Unknow size string: ${sizeString}`);
  }
};
// Sort a list of size strings from smallest to largest
// using Array.sport and our size compare function
const sizeList = ['Large', 'Small', 'Medium'];
sizeList.sort(sizeCompareFunction);
console.log(sizeList); // ['Small', 'Medium', 'Large']

Closing words

That’s a wrap, we covered how you can sort with the default sort method in Apex and the Comparable interface. You saw how you can go beyond the default sort with custom comparators and we briefly covered JavaScript sorting.

Philippe Ozil is a Principal Developer Advocate at Salesforce where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full stack developer and enjoys working on DevOps, robotics, and VR projects. Follow him on Twitter @PhilippeOzil.

Comment(1)

  1. Reply
    Master in Salesforce Apex collection - Apex Hours says:

    […] first choice when it comes to sorting things in Apex is generally to write a SOQL query with an ORDER BY clause. However, sometimes you […]

Post a comment