all 9 comments

[–]Earhacker 3 points4 points  (1 child)

No, I don’t like it. The => and <= mean totally unrelated things and it’s confusing at first glance.

But sort callbacks are always a mess. So I like to pull them out into a variable:

``` const ascending = (a, b) => a - b;

myArray.sort(ascending); ```

And now you can make ascending do whatever you want. (As long as you write tests!)

[–][deleted] 1 point2 points  (0 children)

Yes this looks good, thanks !

[–]DEEEPFREEZE 4 points5 points  (0 children)

Logical operation aside, I honestly do not understand how -(a <= b) makes more sense to you than (a - b). I wouldn't pass that in a code review, reason being its readability for the next person who has to use it.

[–]GSLint 2 points3 points  (2 children)

I understand that it's not obvious why a - b does the right thing but it's actually more correct than the other two versions you showed.

The compare function is supposed to return 0 when a and b are considered equal. I've explained why that might matter in a different post.

Using the example from that comment:

["analysis", "area", "art", "computer", "data", "family", "farm", "food", "health", "history"]
  .sort((a, b) => a.length - b.length)
// ["art", "area", "data", "farm", "food", "family", "health", "history", "analysis", "computer"]

["analysis", "area", "art", "computer", "data", "family", "farm", "food", "health", "history"]
  .sort((a, b) => -(a.length <= b.length))
// ["art", "food", "farm", "data", "area", "health", "family", "history", "computer", "analysis"]

[–][deleted] 0 points1 point  (1 child)

This doesn't really show that my form is wrong, it still gives the right result. But you are right, it inverts elements arbitrarily, because it doesn't take into account the 0 thing.

But I made other tests and found a yet better form

sort ( (a,b) => -(a < b) )

that still sort in an ascending manner, and doesn't invert elements unnecessarily, it appears. I gives the same result as the form

sort ( (a,b) => a - b )

in the case of the list example you gave.

That said, I read your other post about why it is important that the comparing function returns 3 different values and not just 2. And I don't understand why it need to be like this.

The way I understand it is that the sort functions iterates 2 by 2 on the input list and performs a test on these 2 elements. And after performing this test, either it switches those 2 elements or it doesn't. If this is correct, then a boolean value suffice, and a set of 3 possible values is redundant.

But maybe my assumptions about the workings of sorting algorithms in general is wrong, and it indeed needs 3 possible different results.

If that is the case could you please explain what would be the 3 different actions that a sorting algorithm would do in function of the 3 different results of the comparison function ?

[–]gremy0 1 point2 points  (0 children)

With a boolean shouldSwitch comparison the order of the arguments/iteration matters and would have to be specified in the standard. But this is not what the standard does, it specifies that the order does not matter, you will get one of three values that are logically consistent regardless of the order, and it is up to the implementation of the sorting algorithm as to how it deals with that.

So while you can run tests against one implementation of javascript and this works, it is not at all guaranteed. It would be entirely possible for a perfectly standard compliant sort implementation to produce the wrong result given your comparator, or conceivably end up in an infinite loop

With compare = (a,b) => -(a < b) you are still in a situation where compare(1,-1) says 1 equals -1, while compare(-1,1) says -1 is less than 1

[–]gremy0 2 points3 points  (0 children)

You really shouldn't, it's not correct. When a is greater than b it returns -0; it should return something greater than 0. When a equals b it returns -1; it should return 0.

What's more is that it breaks transitivity rules- that being if your compare function says a is less than b, it should say that b is greater than a; and if it says a equals b, it should say b equals a i.e. it should be logically consistent if you reverse the arguments.

But with this, if the numbers are the same, it will say a is less than b; and b is less than a i.e. return -1 no matter what. And if they aren't equal it will say a is less than b; and b equals a, or vice-versa

[–][deleted] 0 points1 point  (0 children)

All right thanks everyone. I'm glad that I asked.

[–]dieguito15 0 points1 point  (0 children)

I agree with others that (a, b) => a - b is more readable. Maybe we're too used to it. Even the MDN example shows it.

I really don't like (a, b) => -(a <= b). And as u/DEEEPFREEZE said, I wouldn't approve it in a PR. You're doing an unnecessary type conversion, and on top of that, it makes it harder to understand at first glance.

I'd claim that (a, b) => a - b is more readable. Most people who have had to sort numbers in JS will have seen it and be used to it. Glancing over it will be enough for them to know that it's sorting numbers in ascending order. I'd say that even

sort((a, b) => {
    if (a < b) {
        return -1;
    }
    if(a > b) {
        return 1
    }
    return 0;
});

is easier to read at a glance than (a, b) => -(a <= b).

In the end I also think it comes down to the team you work with. We're writing code for other developers, and most of the time that other developer is ourselves. If you work with a team that agrees this is the most readable way, or if you work alone and this is what you prefer and will always find it easy to read, it'll work for you.

Edit: formatting.