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.