dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.21k stars 1.57k forks source link

List.sort() should make the compare function optional. #1235

Closed munificent closed 9 years ago

munificent commented 12 years ago

Every time I call List.sort(), I end up doing:

    list.sort((a, b) => a.compareTo(b));

It would be really nice if the compare argument was optional and defaulted to that.

rakudrama commented 12 years ago

I keep copying this function into wherever I need sorting.

List sorted(Iterable input, [compare, key]) {   comparator(compare, key) {     if (compare == null && key == null)       return (a, b) => a.compareTo(b);     if (compare == null)       return (a, b) => key(a).compareTo(key(b));     if (key == null)       return compare;     return (a, b) => compare(key(a), key(b));   }   List copy = new List.from(input);   copy.sort(comparator(compare, key));   return copy; }

Example usage:

for (Element e in sorted(elements, key: (e) => e.name)) {   ... }

I find I use key: just as often as compare:, so I would encourage the more versatile interface. The sort algorithm might be able to exploit a separate key function.

dgrove commented 12 years ago

Issue #3432 has been merged into this issue.

lrhn commented 12 years ago

Issue #5870 has been merged into this issue.

lrhn commented 12 years ago

Added Accepted label.

lrhn commented 12 years ago

For the key-sorting, it seems to be a property of the comparison, not of the sort. What you do is exactly the correct thing: You create a new compare function from an old one. That is, if you have a:

  Comparator projectCompare(key(var object), 
                           [Comparator compare = Comparable.compare]) {
    return (a,b) => compare(key(a), key(b));
  }

(name subject to change and/or ridicule) you don't need to change sort.

In some cases, that's too slow - you don't want to call the key function twice for each comparison. In that case you need a separate algorithm that optimizes differently, but which one will not be something that can be answered generally.

alan-knight commented 12 years ago

I find that with key-sorting, the more useful generalization is to multiple keys. If you're doing a lot of sorting, writing the comparison that if these two are equal, compare based on that one, and so forth, is tedious. So being able to say to sort on different criteria by just specifying the way to get the sortable keys is quite useful. But that doesn't necessarily require any changes to sorting itself, it's just a convenience for generating the compare function. And a call operator would probably be useful for that.

justinfagnani commented 12 years ago

See Guava's Ordering class for a nice way to handle projections, compound comparators, nulls, etc.

http://code.google.com/p/guava-libraries/wiki/OrderingExplained

alan-knight commented 12 years ago

That looks powerful, although quite verbose. My standard of comparison is more like    people sorted: #lastName ascending, #firstName, #middleName

justinfagnani commented 12 years ago

Well, it's Java, it has to be verbose :) In Dart I'm sure we could come up with a much more concise analog.

DartBot commented 11 years ago

This comment was originally written by devdanke...@gmail.com


I support making the sort() comparator optional. I like the design principle, "Make the common case simple". I also agree with the other commentor who complained about Java's excessive verboseness. Eliminating and preventing unneeded verboseness makes Dart more accessible to new developers and less annoying to experienced developers.

lrhn commented 11 years ago

The compare function argument of List.sort is now optional. We have no plans of adding further functionality to sort.


Added Fixed label.

kamleshwebtech commented 4 years ago

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

maxdiable commented 9 months ago

hi, it's possible compare a nullable property ?

munificent commented 9 months ago

it's possible compare a nullable property ?

Nullable types don't implement Comparable<T> directly, but you can sort nullable values by providing your own comparison function that handles the null directly and then it's up to you do decide whether you consider null to come before or after other values:

main() {
  var numbers = [5, 3, null, 2, null, 1, 4];

  numbers.sort((a, b) {
    if (a == null) {
      if (b == null) {
        return 0; // Null is equal to itself.
      } else {
        return -1; // Sort null first.
      }
    } else {
      if (b == null) {
        return 1; // Sort null first.
      } else {
        return a.compareTo(b);
      }
    }
  });

  print(numbers);
}

This prints [null, null, 1, 2, 3, 4, 5]. If you want to use the new Dart 3.0 features, you could write:

main() {
  var numbers = [5, 3, null, 2, null, 1, 4];

  numbers.sort((a, b) {
    return switch ((a, b)) {
      (null, null) => 0, // Null is equal to itself.
      (int _, null) => 1, // Sort null first.
      (null, int _) => -1, // Sort null first.
      (int a, int b) => a.compareTo(b),
    };
  });

  print(numbers);
}
maxdiable commented 9 months ago

Very nice

Thanks 👍

Il gio 25 gen 2024, 00:32 Bob Nystrom @.***> ha scritto:

it's possible compare a nullable property ?

Nullable types don't implement Comparable directly, but you can sort nullable values by providing your own comparison function that handles the null directly and then it's up to you do decide whether you consider null to come before or after other values:

main() { var numbers = [5, 3, null, 2, null, 1, 4];

numbers.sort((a, b) { if (a == null) { if (b == null) { return 0; // Null is equal to itself. } else { return -1; // Sort null first. } } else { if (b == null) { return 1; // Sort null first. } else { return a.compareTo(b); } } });

print(numbers); }

This prints [null, null, 1, 2, 3, 4, 5]. If you want to use the new Dart 3.0 features, you could write:

main() { var numbers = [5, 3, null, 2, null, 1, 4];

numbers.sort((a, b) { return switch ((a, b)) { (null, null) => 0, // Null is equal to itself. (int , null) => 1, // Sort null first. (null, int ) => -1, // Sort null first. (int a, int b) => a.compareTo(b), }; });

print(numbers); }

— Reply to this email directly, view it on GitHub https://github.com/dart-lang/sdk/issues/1235#issuecomment-1909098216, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABTSU566DDQ6RQRG5HUEBTYQGKY7AVCNFSM4KKVUU52U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOJQHEYDSOBSGE3A . You are receiving this because you commented.Message ID: @.***>

lrhn commented 8 months ago

One can also create helper functions like:

int Function(T?, T?) compareNullsFirst<T extends Object?>(
        int Function(T, T) compare) =>
    (T? a, T? b) => a == null ? b == null ? 0 : -1 : b == null ? 1 : compare(a, b);
int Function(T?, T?) compareNullsLast<T extends Object?>(
        int Function(T, T) compare) =>
    (T? a, T? b) => a == null ? b == null ? 0 : 1 : b == null ? -1 : compare(a, b);

which allows any other comparison function to be extended to nullable values.

void main() {
  print([6, 1, 3, null, 4, null, 2]..sort(
      compareNullsFirst(Comparable.compare))); // [null, null, 1, 2, 3, 4, 6]
  print([6, 1, 3, null, 4, null, 2]..sort(
      compareNullsLast(Comparable.compare))); // [1, 2, 3, 4, 6, null, null]
}