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

ListFlutterSortingDart

List Problem Overview


How to sort a list of objects by the alphabetical order of one of its properties (Not the name but the actual value the property holds)?

List Solutions


Solution 1 - List

You can pass a comparison function to List.sort.

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

Solution 2 - List

If you want to sort the object "objects" by the property "name" do something like this

objects.sort((a, b) {
  return a.value['name'].toString().toLowerCase().compareTo(b.value['name'].toString().toLowerCase());
});    

Solution 3 - List

In general, you can provide a custom comparison function to List.sort.

/// Desired relation | Result
/// -------------------------------------------
///           a < b  | Returns a negative value.
///           a == b | Returns 0.
///           a > b  | Returns a positive value.
///
int mySortComparison(SomeClass a, SomeClass b) {
  final propertyA = someProperty(a);
  final propertyB = someProperty(b);
  if (propertyA < propertyB) {
    return -1;
  } else if (propertyA > propertyB) {
    return 1;
  } else {
    return 0;
  }
}

list.sort(mySortComparison);

If you're sorting some custom class you own, you alternatively could make your class implement the Comparable interface:

class MyCustomClass implements Comparable<MyCustomClass> {
  ...

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

and then you can use list.sort() directly without supplying a callback.

Note that if you're sorting by a single property that already implements the Comparable interface, implementing the comparison functions is much simpler. For example:

class MyCustomClass implements Comparable<MyCustomClass> {
  ...

  @override
  int compareTo(MyCustomClass other) =>
    someProperty.compareTo(other.someProperty);
}

Reversing

If you want to reverse the sort order, you can make your comparison function return a value with the opposite sign. Alternatively just explicitly reverse the list after sorting:

list = (list..sort()).reversed.toList();

Sorting by multiple properties (a.k.a. subsorting)

If you want to sort by multiple properties, a general way is to perform a stable sort for each property in reverse order of importance. For example, if you want to sort names primarily by surname and then subsort within surnames by given name, then you would first sort by given names, and then perform a stable sort by surname. See below for how to perform a stable sort.

Alternatively, you could sort with a comparison function that accounts for multiple properties. For example:

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);
}

Okay, I want a stable sort

List.sort is not guaranteed to be a stable sort. If you need a stable sort, package:collection provides insertionSort and mergeSort implementations that are stable.


But comparing might be expensive

Suppose you have a custom comparison function that looks something like:

int compareMyCustomClass(MyCustomClass a, MyCustomClass b) {
  var a0 = computeValue(a);
  var b0 = computeValue(b);
  return a0.compareTo(b0);
}

Sorting might call computeValue() on each element multiple times, which is particularly wasteful if computeValue() is expensive. In such cases, a Schwartzian transform could be faster (at the expense of using more memory). This approach maps your objects to directly sortable keys, sorts the keys, and extracts the original objects. (This is how Python's sort and sorted functions work.)

Here's one possible implementation:

class _SortableKeyPair<T, K extends Comparable<Object>>
    implements Comparable<_SortableKeyPair<T, K>> {
  _SortableKeyPair(this.original, this.key);

  final T original;
  final K key;

  @override
  int compareTo(_SortableKeyPair<T, K> other) => key.compareTo(other.key);
}

/// Returns a sorted *copy* of [items] according to the computed sort key.
List<E> sortedWithKey<E, K extends Comparable<Object>>(
  Iterable<E> 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,
  ];
}

void main() {
  final list = <MyCustomClass>[ ... ];
  final sorted = sortedWithKeys(list, computeValue);
}

Solution 4 - List

Here is my contribution to this good question. If someone is facing difficulty to understand how the @Nate Bosch answer is working & you want to sort your custom model class list then you can do this way.

1. You have to implement Comparable abstract class in your model class. It has the method compareTo which you have to override. For example, I have this StudentMarks model class which has marks property in it.

class StudentMarks implements Comparable {
  int marks;

  StudentMarks({
    this.marks,
  });

  
  @override
  int compareTo(other) {
    
    if (this.marks == null || other == null) {
      return null;
    }

    if (this.marks < other.marks) {
      return 1;
    }

    if (this.marks > other.marks) {
      return -1;
    }

    if (this.marks == other.marks) {
      return 0;
    }

    return null;
  }
}

2. Now you can call compareTo method inside the sort method.

void _sortStudents({bool reversed: false}) {
    _students.sort((a, b) {
      return a.compareTo(b);
    });
    
    if (reversed) {
      _students = _students.reversed.toList();
    }

    setState(() {});
  }

Refer to this link you want to know more about the Comparable class

https://api.dart.dev/stable/2.1.0/dart-core/Comparable-class.html

Solution 5 - List

Immutable extension sortedBy for List.

extension MyIterable<E> on Iterable<E> {
  Iterable<E> sortedBy(Comparable key(E e)) =>
      toList()..sort((a, b) => key(a).compareTo(key(b)));
}

And use

list.sortedBy((it) => it.name);

Solution 6 - List

Using Comparatorfunction, sort Users by id.

Comparator<UserModel> sortById = (a, b) => a.id.compareTo(b.id);
users.sort(sortById);

Now we can sort it in reversed/descending order.

users = users.reversed.toList();

Solution 7 - List

Its worked for me:

myList..sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));

Solution 8 - List

Similar to @pavel-shorokhovs answer, but strongly typed:

extension IterableExtensions<T> on Iterable<T> {

  Iterable<T> sortBy<TSelected extends Comparable<TSelected>>(
      TSelected Function(T) selector) =>
  toList()..sort((a, b) => selector(a).compareTo(selector(b)));

  Iterable<T> sortByDescending<TSelected extends Comparable<TSelected>>(
      TSelected Function(T) selector) =>
  sortBy(selector).toList().reversed;

}

Solution 9 - List

To sort it in reverse order :

list.sort((a, b) {
          return b.status.toLowerCase().compareTo(a.status.toLowerCase());
        });

Solution 10 - List

i had fpgrowth machine learning output/result with each element of list contains another list and frequency field i was to sort by frequency in descending order so i used a bit of recursion for that try it might work i know i am late but i am posting maybe someone else could benefit.

 sort(List<FrequentItem> fqItems) {
    int len = fqItems.length;
    if(len==2){
      if(fqItems[0].frequency>fqItems[1].frequency){
        sortedItems.add(fqItems[0]);
        sortedItems.add(fqItems[1]);
      }else{
        sortedItems.add(fqItems[1]);
        sortedItems.add(fqItems[0]);
      }
      return;
    }else{
      FrequentItem max = fqItems[0];
      int index =0;
      for(int i=0;i<len-2;i++){
        if(max.frequency<fqItems[i+1].frequency){
          max = fqItems[i+1];
          index = i+1;
        }
      }
      sortedItems.add(max);
      fqItems.removeAt(index);
      sort(fqItems);
    }


  }

Solution 11 - List

What's more, you can use Comparable.compare for more clear, for example:

class _Person {
  final int age;
  final String name;
  _Person({required this.age, required this.name});
}

void _test() {
  final array = [
    _Person(age: 10, name: 'Dean'),
    _Person(age: 20, name: 'Jack'),
    _Person(age: 30, name: 'Ben'),
  ];

  // ascend with age
  // Dean Jack Ben
  array.sort((p1, p2) {
    return Comparable.compare(p1.age, p2.age);
  });

  // decend with age
  // Ben Jack Dean
  array.sort((p1, p2) {
    return Comparable.compare(p2.age, p1.age);
  });

  // ascend with name
  // Ben Dean Jack
  array.sort((p1, p2) {
    return Comparable.compare(p1.name, p2.name);
  });
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNomnomView Question on Stackoverflow
Solution 1 - ListNate BoschView Answer on Stackoverflow
Solution 2 - ListConnerView Answer on Stackoverflow
Solution 3 - ListjamesdlinView Answer on Stackoverflow
Solution 4 - ListRopali MunshiView Answer on Stackoverflow
Solution 5 - ListPavel ShorokhovView Answer on Stackoverflow
Solution 6 - ListSandeep MauryaView Answer on Stackoverflow
Solution 7 - ListmostaphaView Answer on Stackoverflow
Solution 8 - ListNiklas RaabView Answer on Stackoverflow
Solution 9 - ListTanmoy KarmakarView Answer on Stackoverflow
Solution 10 - ListZeeshan MehdiView Answer on Stackoverflow
Solution 11 - List无夜之星辰View Answer on Stackoverflow