Sort a list of objects in Flutter (Dart) by property value

Introduction

In Dart, sorting lists of custom objects is a common operation. You can pass a comparison function to List.sort() or make your class implement the Comparable interface. In this post, we’ll explore these options and provide examples for each.

Passing a Comparison Function to List.sort()

You can sort a list of custom objects using the List.sort() method by passing a comparison function as a callback. The comparison function should return a negative value if the first object is less than the second, zero if they are equal, and a positive value if the first object is greater.

someObjects.sort((a, b) => a.someProperty.compareTo(b.someProperty));

Implementing the Comparable Interface

An alternative approach is to make your custom class implement the Comparable interface. This allows you to use List.sort() directly without supplying a callback.

class MyCustomClass implements Comparable {
  ...

  @override
  int compareTo(MyCustomClass other) {
    if (someProperty < other.someProperty) {
      return -1;
    } else if (someProperty > other.someProperty) {
      return 1;
    } else {
      return 0;
    }
  }
}

With this approach, you can sort a list of MyCustomClass objects using List.sort() as follows:

list.sort();

Sorting by Multiple Properties (Subsorting)

When sorting by multiple properties, also known as subsorting, the approach depends on the specific requirements. One common method is to perform a stable sort for each property in reverse order of importance.

A General Approach

This involves sorting the list first by one property, then by another, and so on. The idea is to make the “subsort” as efficient as possible by minimizing the number of comparisons needed.

class Name {
  Name({String? surname, String? givenName})
    : surname = surname ?? "",
      givenName = givenName ?? "";

  final String surname;
  final String givenName;
}

int compareNames(Name name1, Name name2) {
  var comparisonResult = name1.surname.compareTo(name2.surname);
  if (comparisonResult != 0) {
     return comparisonResult;
  }
  // Surnames are the same, so subsort by given name.
  return name1.givenName.compareTo(name2.givenName);
}

Using package:collection’s Extension

package:collection provides an extension to chain comparison functions, making it easier and less error-prone to combine them. This allows you to sort by multiple properties in a straightforward way.

import 'package:collection/collection.dart';

int compareSurnames(Name name1, Name name2) => name1.surname.compareTo(name2.surname);

int compareGivenNames(Name name1, Name name2) =>
    name1.givenName.compareTo(name2.givenName);

list.sortBy((element) => [compareSurnames(element, element2),
                         compareGivenNames(element, element2)])
      .toList();

Using package:basics’ List.sortBy Extension Method

package:basics also provides a List.sortBy extension method that effectively does the same thing as above.

import 'package:basics/basics.dart';

list.sortBy((element) => [compareSurnames(element, element2),
                         compareGivenNames(element, element2)])
      .toList();

Sorting with an Expensive Custom Comparison Function

If your comparison function is expensive to compute and the sorting process calls it multiple times for the same object, you can use a Schwartzian transform to avoid redundant computations.

class _SortableKeyPair>
    implements Comparable<_SortableKeyPair> {
  _SortableKeyPair(this.original, this.key);

  final T original;
  final K key;

  @override
  int compareTo(_SortableKeyPair other) => key.compareTo(other.key);
}

/// Returns a sorted *copy* of [items] according to the computed sort key.
List sortedWithKey>(
  Iterable items,
  K Function(E) toKey,
) {
  final keyPairs = [
    for (var element in items) _SortableKeyPair(element, toKey(element)),
  ]..sort();

  return [
    for (var keyPair in keyPairs) keyPair.original,
  ];
}

Using package:dartbag’s sortWithKey Function

package:dartbag provides a sortWithKey function that effectively does the same thing as above.

final sorted = sortedWithKeys(list, computeValue);

Conclusion

In conclusion, sorting lists of custom objects in Dart can be done using List.sort() with a comparison function or by implementing the Comparable interface. When sorting by multiple properties, you can use a stable sort for each property in reverse order of importance. If your comparison function is expensive to compute, consider using a Schwartzian transform to avoid redundant computations.