Dart - Sorting List with Comparator and Comparable

If you're using Dart and you need to set how a List should be sorted, this tutorial gives you examples of how to use Comparator and Comparable for sorting. The below examples also work for any Dart frameworks including Flutter.

Dart's List has .sort() method. By default, if you don't pass a Comparator as the argument, it will use natural sort ordering. The .sort() method mutates the List and it returns void.

  List<String> strings = ['woolha', 'dot', 'com'];
  strings.sort();
  print(strings);

The code above sorts the strings alphabetically. Output:

  [com, dot, woolha]

If you need a custom sort ordering, you can pass a Comparator function. It accepts two parameters representing two values to be compared. The function must return an integer. If the result is negative, the first value will be placed before the second value. Otherwise, the second value will appear before the first value in the sorted List. If the result is 0, it means both values are equal in the ordering.

  strings.sort((a, b) => a.length.compareTo(b.length));
  print(strings);

Output:

  [dot, com, woolha]

For List of objects, Comparator can be used as well. For example, there is a class Item

  class Item {
    int id;
    String name;
    int price;
  
    Item({this.id, this.name, this.price});
  }

The code below sorts Item objects based on its price.

  Item item = new Item(id: 1, name: "Item one", price: 1000);
  Item item2 = new Item(id: 2, name: "Item two", price: 2000);
  Item item3 = new Item(id: 3, name: "Item three", price: 500);
  Item item4 = new Item(id: 4, name: "Item four", price: 1000);
  List<Item> items = [item, item2, item3, item4];

  Comparator<Item> priceComparator = (a, b) => a.price.compareTo(b.price);
  items.sort(priceComparator);

  items.forEach((Item item) {
    print('${item.id} - ${item.name} - ${item.price}');
  });

Output:

  3 - Item three - 500
  1 - Item one - 1000
  4 - Item four - 1000
  2 - Item two - 2000

You can have multiple Comparator instances

  Comparator<Item> nameComparator = (a, b) => a.name.compareTo(b.name);
  items.sort(priceComparator);

  items.forEach((Item item) {
    print('${item.id} - ${item.name} - ${item.price}');
  });

Output:

  4 - Item four - 1000
  1 - Item one - 1000
  3 - Item three - 500
  2 - Item two - 2000

Using Comparable

Another alternative is using Comparable interface. It's suitable for a type that has intrinsic order. By implementing Comparable, you can define the natural sort ordering for the type. However, if the type can be orderd in multiple ways, it's better to use Comparator instead.

To implement Comparable, you have to override compareTo method which has one parameter of the type to be compared. Like Comparator, it returns an integer with the same meaning. Negative value means the value (this instance) will appear first in ordering, while positive value means the opposite.

  class Item implements Comparable<Item> {
    int id;
    String name;
    int price;
  
    Item({this.id, this.name, this.price, });
  
    @override
    int compareTo(Item other) {
      return price - other.price;
    }
  }

If you call .sort() without argument, comparison will use the logic defined in .compareTo()

  items.sort();

  items.forEach((Item item) {
    print('${item.id} - ${item.name} - ${item.price}');
  });

The output should be the same as the previous example which uses Comparator.

If you remove the Comparable, you'll get the following error:

  Unhandled exception:
  type 'Item' is not a subtype of type 'Comparable<dynamic>'

If you need to compare more than one attribute, you can modify the compareTo function. The below example compares by price, then by name if the prices are the same.

  @override
  int compareTo(Item other) {
    int priceDiference = price - other.price;

    return priceDifference != 0
        ? priceDifference
        : this.name.compareTo(other.name);
  }

Output:

  3 - Item three - 500
  4 - Item four - 1000
  1 - Item one - 1000
  2 - Item two - 2000

If the class only has one natural order, use Comparable. Otherwise, you need to create a Comparator for each order.