JS++ makes object-oriented sorting easy with the IComparable<T> interface and the Comparison enumeration for type-safe (and readable) comparisons.
Here’s the code. (Don’t worry; I’ll dissect it.)
import System; class Employee : IComparable<Employee> { private string firstName; private string lastName; public Employee(string firstName, string lastName) { this.firstName = firstName; this.lastName = lastName; } public Comparison compare(Employee that) { // Sort by employee surname return this.lastName.compare(that.lastName); } public override string toString() { return this.firstName + " " + this.lastName; } } Employee zig = new Employee("Zig", "Ziglar"); Employee john = new Employee("John", "Smith"); Employee abe = new Employee("Abe", "Lincoln"); Employee[] employees = [ zig, john, abe ]; employees.sort(); Console.log(employees.join(", ")); // Output: // Abe Lincoln, John Smith, Zig Ziglar
This is beautiful, object-oriented code. All of the custom sorting logic is one line of code. Let’s break down how that happens step-by-step.
1. Implement IComparable<T>
The first step is to implement the IComparable<T> interface. The interface provides only one method to implement: compare.
compare
expects the Comparison enumeration as a result. As we can see from the documentation, Comparison
can have three possible results: LESS_THAN, GREATER_THAN, and EQUAL. While Java/C# expect -1, 0, and 1, JS++ gives you type-safe and readable comparisons.
IComparable<T> and Comparison form the basis for custom sorting.
2. Determine how to sort
We want to sort Employee
objects based on the employee’s last name. In order to do this, we want to compare strings and sort in alphabetical order. While we can do this manually, the JS++ Standard Library already provides these comparisons for us for primitive types.
All primitive types in JS++ are auto-boxed. (Don’t worry, it gets optimized away.) In addition, all primitive types implement IComparable<T> (which provides the compare
method).
Thus, since all primitive types provide the compare
method, sorting is as easy as this one line of code:
return this.lastName.compare(that.lastName);
This is calling the System.String.compare
method, which compares strings lexicographically (in alphabetical order). (Likewise, if you wanted to compare by employee ID number, you might declare an unsigned int
and use System.UInteger32.compare.)
Thus, our sorting code and implementation of IComparable<T>.compare is just:
public Comparison compare(Employee that) { // Sort by employee surname return this.lastName.compare(that.lastName); }
3. Define toString() Behavior
In addition, we want to be able to easily visualize our sorted arrays. Therefore, we should define how our Employee
class looks when converted to a string so we can easily call System.Console.log
on it.
JS++ internal types use a “unified type system” where everything inherits from System.Object. If we look at the System.Object.toString
documentation, we can see that System.Object.toString
is a virtual
method based on its signature:
public virtual string toString()
We override it with this code:
public override string toString() { return this.firstName + " " + this.lastName; }
Thus, whenever we want a string representation of our Employee
object, we will get the employee’s first name followed by his last name. This will help us visualize our sorted employees.
4. Instantiate some Employees
The next lines of code instantiate the Employee
class and inserts them in an array:
Employee zig = new Employee("Zig", "Ziglar"); Employee john = new Employee("John", "Smith"); Employee abe = new Employee("Abe", "Lincoln"); Employee[] employees = [ zig, john, abe ];
Currently, the array is unsorted, and “Zig Ziglar” will be the first element.
5. Sort the Array
Sorting is as simple as one line of code:
employees.sort();
It’s just one line of code because we implemented IComparable<T>. Instead of implementing IComparable<T>, we could have also used the other overload of Array.sort, which expects a callback:
employees.sort(Comparison(Employee a, Employee b) { return a.lastName.compare(b.lastName); });
The callback allows flexibility; for example, you may choose to sort by employee first name in some cases.
Implementing IComparable<T> simply provides a default sort so you can use System.Array.sort without a callback. These are the signatures for the System.Array.sort overloads:
public T[] sort() where T: IComparable<T>
public T[] sort(Comparison(T element1, T element2) comparator)
Thus, if you do not provide a callback, you are using the overload that expects a class implementing IComparable<T>. If you try to sort objects whose respective classes do not implement the IComparable interface, you’ll receive an error:
[ ERROR ] JSPPE5056: System.Array.sort()' can only sort classes implementing 'IComparable '. Please implement 'IComparable ' for `Employee' or use 'System.Array .sort(Comparison(T element1, T element2) comparator) at line 23 char 0 at test.js++
6. Print the Result
The final step is to just print the result:
Console.log(employees.join(", "));
Et voila!
(The toString
method we implemented earlier will get called for each element that gets joined. Thus, you get a readable output.)