2

I try to sort my array of objects :

object:

interface IFriend {
  _id: string;
  name: string;
  surname: string;
  avatar: string;
  online: boolean;
}

I want to do it in order - online, surname, name. I tried this :

  friendsCopy
    .sort((a: IFriend, b: IFriend) =>
      a.online === b.online ? 0 : a.online ? -1 : 1
    )
    .sort((a: IFriend, b: IFriend) => a.surname.localeCompare(b.surname, "pl"))
    .sort((a: IFriend, b: IFriend) => a.name.localeCompare(b.name, "pl"));

But it doesn't work well

poldeeek
  • 313
  • 2
  • 16
  • 1
    sort function doesn't respect the current order :). this code you write probably only sort's the array by `name` property. – halilcakar Oct 18 '20 at 14:10
  • @HalilCakar say sortin in js is not "stable" you have to use some stable sort algoritms like merge sort [look](https://stackoverflow.com/questions/1427608/fast-stable-sorting-algorithm-implementation-in-javascript) – Robert Oct 18 '20 at 14:13
  • Well actually it's quite stable for me :D it's just when u sort after sort, the first sort you have made is not relevant to the result =) – halilcakar Oct 18 '20 at 14:14
  • [ES 10 feature - stable sort js](https://buginit.com/javascript/stable-sort-array-javascript-es10/) so is stable in chorme :P (now) – Robert Oct 18 '20 at 14:15
  • Please be aware this type of sorting requires stable sort, most modern browsers do support it, but of course IE11 doesn't... https://caniuse.com/mdn-javascript_builtins_array_sort_stable It is possible to do this with one sort, if maximum browser compatibly is required. Or maybe a pollyfill. – Keith Oct 18 '20 at 14:16

2 Answers2

5

Because you are sorting in the wrong direction.

If you reverse the functions then it should give your desired output:

friendsCopy
    .sort((a: IFriend, b: IFriend) => a.name.localeCompare(b.name, "pl"))
    .sort((a: IFriend, b: IFriend) => a.surname.localeCompare(b.surname, "pl"))
    .sort((a: IFriend, b: IFriend) =>
      a.online === b.online ? 0 : a.online ? -1 : 1
    );

Sort by name, then sort by surnames, then sort by who is online. The final list will be online -> surname -> name

Dylan Kerler
  • 2,007
  • 1
  • 8
  • 23
2

As mentioned in the other answer, you are sorting in the reverse direction. The final sort is prioritized over the others.

You can club them all to a single sort using the || operator.

friendsCopy.sort((a, b) => b.online - a.online 
                || a.surname.localeCompare(b.surname, "pl") 
                || a.name.localeCompare(b.name, "pl")
    )

First, sort the array by online property. You can simply subtract the booleans since they get coerced to numbers and result in -1, 0 or 1 based on their value.

true - false === 1
false - true === -1
true - true === 0
false - false === 0

All the online: true objects will be moved to the front of the array. If both the objects being compared have the same online value, then the subtraction returns 0 and the || operator moves to the next condition and sorts them based on surname. Again, if the objects have the same surname property, then finally the objects are sorted based on name property.

adiga
  • 34,372
  • 9
  • 61
  • 83
  • Most Modern JS engines use stable sort, so the previous sort is not messed up, it's just the OP has sorted by name, surname, online instead. But personally I would use a single sort anyway. IOW: Why do 3 sorts when 1 will do. So I'm up-voting this one. – Keith Oct 18 '20 at 14:48
  • @Keith maybe *messed up* is not the correct word. If the compare function returns `0`, they will keep their relative positions. But, the newer sort will ignore the previous `sort` if it returns -1 or 1 – adiga Oct 18 '20 at 14:51