'How to sort a list based on two values
I have a list in which I want to sort based on two values date and the status like this:
pollsList.sort((a, b) => b.active.compareTo(a.active));
pollsList.sort((a, b) {
return b.actualStartDatetime.compareTo(a.actualStartDatetime);
});
where active == 90 if it's active and -90 if it's not.
I want to sort the list based on the date and then based on the date.
I want the newest at the top and always the closed to be after the active items.
But this will result the list only being sorted by the date. How do I sort it by both values?
Solution 1:[1]
You want to sort the list so that a later start time is sorted before an earlier start time, and if both elements have the same start time, then sort active elements before inactive elements.
What you have attempted is to sort the list twice. That only works if the sorting algorithm is stable, that is, if elements that are equal wrt. the second sort comparison stay in the order they were put by the first sort. Dart's sort does not promise to be stable, so this is not a safe approach. Even if it appears to work, it is quite possible to have a sorting algorithm that is stable for small lists and not for larger lists.
What you should do instead is to create a single comparison function that does both compares at once. First compare the start times. If those are not equal, you are done, otherwise compare the activities as well.
pollsList.sort((a, b) {
int cmp = b.actualStartDatetime.compareTo(a.actualStartDatetime);
if (cmp != 0) return cmp;
return b.active.compareTo(a.active);
});
If you want all inactive elements to be sorted before all active elements, and then have each of these groups sorted by start time, you do the comparison in the opposite order:
pollsList.sort((a, b) {
int cmp = b.active.compareTo(a.active);
if (cmp != 0) return cmp;
return b.actualStartDatetime.compareTo(a.actualStartDatetime);
});
Solution 2:[2]
i solve this by making two columns into one. "${a.active}${a.actualStartDatetime}" and compare it to b.
data.sort((a, b) => ("${a.active}${a.actualStartDatetime}").toString()
.compareTo(("${b.status}${b.actualStartDatetime.toUpperCase()}").toString()));
Solution 3:[3]
A more generic way to sort lists based on two or more criteria consists of using a comparator function.
The sort method that is available for Dart lists accepts a comparator function as an optional parameter.
In the example below a list with entries of type MyWidget (which have the attributes: title, time, and status are sorted in different ways without the need to create and merge separate lists.
/// The current status of the widget.
enum Status { open, closed }
// A comparator function for object of type `Status`.
int statusComparator(left, right) {
if (left == right) return 0;
if (left == Status.open) return -1;
return 1;
}
/// DateTime Comparator based on year-month-day only.
int timeComparator(left, right) {
var result = -left.year.compareTo(right.year);
result = result == 0 ? -left.month.compareTo(right.month) : result;
result = result == 0 ? -left.day.compareTo(right.day) : result;
return result;
}
/// Sample Widget Class
/// Implements Comparable and requires a `compareTo` method.
class MyWidget implements Comparable {
MyWidget(this.title, {this.status = Status.open}) : time = DateTime.now();
late DateTime time;
String title;
Status status;
@override
String toString() {
return '$title ${time.day}-${time.month}-${time.year} ${status.name}';
}
/// Default method used for sorting by title > time > status.
@override
int compareTo(other) {
int result = title.compareTo(other.title);
result = result == 0 ? timeComparator(time, other.time) : result;
result = result == 0 ? statusComparator(status, other.status) : result;
return result;
}
/// Additional comparator used for sorting by status > time > title.
static int openFirstComparator<T extends MyWidget>(T left, T right) {
var result = statusComparator(left.status, right.status);
result = result == 0 ? timeComparator(left.time, right.time) : result;
return result == 0 ? left.title.compareTo(right.title) : result;
}
}
void main() {
var list = [
MyWidget('Carol', status: Status.closed)..time = DateTime(2018),
MyWidget('Carol')..time = DateTime(2018),
MyWidget('Carol')
..time = DateTime(2022)
..status = Status.closed,
MyWidget('Bernard')..time = DateTime(2020),
MyWidget('Anne'),
MyWidget('Anne', status: Status.closed),
MyWidget('Bernard', status: Status.closed),
];
/// Default sorting by title > date > status using the `compareTo` method.
print('Sorted by title > date > status');
print(list..sort());
print('');
/// Sorted by status, date and title using a custom comparator.
print('Sorted by status > date > title');
print(list..sort(MyWidget.openFirstComparator));
}
Running the program displays:
$ dart main.dart
Sorted by title > date > status
[Anne 2-3-2022 open, Anne 2-3-2022 closed, Bernard 2-3-2022 closed, Bernard 1-1-2020 open, Carol 1-1-2022 closed, Carol 1-1-2018 open, Carol 1-1-2018 closed]
Sorted by status > date > title
[Anne 2-3-2022 open, Bernard 1-1-2020 open, Carol 1-1-2018 open, Anne 2-3-2022 closed, Bernard 2-3-2022 closed, Carol 1-1-2022 closed, Carol 1-1-2018 closed]
Tip: To quickly run the code, copy it and paste it into the dartpad editor.
Solution 4:[4]
This is not the most elegant, but it's fairly straight forward and worked for me:
- Divide your original list into 2 new lists based on the first sorting criteria;
- Sort each new list by the second criteria;
- Finally, combine the 2 new lists into a final, sorted list;
For example:
List<Polls> activePollsList;
List<Polls> inactivePollsList;
List<Polls> finalSortedPollsList;
activePollsList.addAll(pollsList.where((Poll poll) => poll.isActive == true));
activePollsList.sort((a, b) => a.actualStartDateTime.compareTo(b.actualStartDateTime));
inactivePollsList.addAll(pollsList.where((Polls poll) => poll.isActive == false));
inactivePollsList.sort((a, b) => a.actualStartDateTime.compareTo(b.actualStartDateTime));
finalSortedPollsList = activePollsList + inActivePollsList;
That finalSortedList will retain the sorting done by the two lists.
Note in the above I'm making the assumption that your pollsList is a list of custom objects called Polls, but you would want to change Poll in the above code to whatever your custom object is named.
Again, not the most elegant solution, but until Dart allows for multiple sorting criteria this worked for me.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Dharman |
| Solution 3 | |
| Solution 4 | stfn3000 |
