0

I have this array of objects with an n and an o property holding an integer.
And I want to sort it by ascending value of the n property, and if two elements have the same value of n, then I want to order them by ascending value of o.

I know that the right way of doing this would be:

const s = [
  { n: 0, o: 0 },
  { n: 2, o: 1 },
  { n: 3, o: 2 },
  { n: 7, o: 3 },
  { n: 4, o: 4 },
  { n: 8, o: 5 },
  { n: 2, o: 6 },
  { n: 0, o: 7 },
  { n: 3, o: 8 },
  { n: 4, o: 9 },
  { n: 8, o: 10 },
  { n: 7, o: 11 },
  { n: 3, o: 12 },
  { n: 3, o: 13 },
];

console.log(s.sort((a, b) => (a.n === b.n ? a.o - b.o : a.n - b.n)));

But since o is basically the index of an object in the array, I thought I could swap a.o - b.o with -1, that is: if a.n === b.n, then "sort a before b", meaning preserve the order between a and b.

But it tuns out that -1 would actually sort by descending value of o:

const s = [
  { n: 0, o: 0 },
  { n: 2, o: 1 },
  { n: 3, o: 2 },
  { n: 7, o: 3 },
  { n: 4, o: 4 },
  { n: 8, o: 5 },
  { n: 2, o: 6 },
  { n: 0, o: 7 },
  { n: 3, o: 8 },
  { n: 4, o: 9 },
  { n: 8, o: 10 },
  { n: 7, o: 11 },
  { n: 3, o: 12 },
  { n: 3, o: 13 },
];

console.log(s.sort((a, b) => (a.n === b.n ? -1 : a.n - b.n)));

While if I try 1 rather than -1 I get the sorting I wanted.

Why do I need to return a positive value to keep the order of the elements, when the docs say the opposite? And can I rely on this behaviour or do I have to return a.o - b.o instead of 1 to consistently get the order I want?

opensorcio
  • 59
  • 2
  • 6
  • Does this answer your question? [How to sort an array of objects by multiple fields?](https://stackoverflow.com/questions/6913512/how-to-sort-an-array-of-objects-by-multiple-fields) – pilchard May 13 '22 at 21:39
  • 1
    You would want to compare them (`a.o - b.o`) instead of returning a fixed value since you don't know which order they are encountered ahead of time. (but you can just use an `OR (||)` short circuit `s.sort((a, b) => a.n - b.n || a.o - b.o)`) – pilchard May 13 '22 at 21:40
  • 2
    @akaAbdullahMateen He's not assuming they're adjacent, but he's assuming that `a` will always be earlier in the array than `b`. Neither assumption is valid. – Barmar May 13 '22 at 21:42
  • The inner workings of the sort function are not specified, just the visible behavior. And the specification doesn't say anything about the order that elements are given to the comparison function. – Barmar May 13 '22 at 21:48
  • @Barmar so that's it? It's just that you can't make assumptions? But the `1` does work. Is it maybe a behaviour that is consistent among browsers even if the spec don't specify it? – opensorcio May 13 '22 at 21:51
  • If the `1` is working consistently it's because of your limited (preordered) input data. Move the bottom `{ n: 3, o: 12 }` to be the first element of the array and you'll see what I mean. – pilchard May 13 '22 at 21:52
  • @pilchard but in my case the second criteria (in the OP i named it `o`) is the index of the array, that is always ordered. So can I rely in `1` if my second criteria is the index? That is can I rely in `1` if I want to order the object by a value and resolve ties by keeping the original order? – opensorcio May 13 '22 at 21:55
  • If it's always ordered you don't have to sort by it at all and can just sort by `a.n`. To say it another way if returning a constant value from the `sort()` callback 'works' then your array was already sorted. – pilchard May 13 '22 at 21:55
  • You're just testing one implementation. It might always pass them in the original order, but some other implementation could be different. – Barmar May 13 '22 at 21:55
  • Note also that JS sort isn't required to be stable. If it were, you could return `0` when `a.n == b.n` – Barmar May 13 '22 at 21:57
  • All right thank you @pilchard and @Barmar. Just to close the issue, can I rely on just sorting by `n` to get the ties resolved by index, or is even this something that depends on the implementation? – opensorcio May 13 '22 at 22:05
  • If the `o` property is always ordered ascending to start, then just sorting by `n` will reliably maintain the order of `o` – pilchard May 13 '22 at 22:07
  • @pilchard thank you so much. I am trusting your assertion, just because I am used to trust people asserting stuff on SO. But if you happen to have a link to some kind of documentation supporting your claims, I would really appreciate if you could drop the link here. Thank you again anyway! – opensorcio May 13 '22 at 22:11
  • 1
    [Stable Array.prototype.sort](https://v8.dev/features/stable-sort#:~:text=All%20major%20JavaScript%20engines%20now,is%20now%20stable%20as%20well.) From the V8 engine blog, and [the spec proposal for stability](https://github.com/tc39/ecma262/pull/1340) (accepted to the spec, but still some [smaller browsers](https://github.com/mozilla/rhino/issues/1151) that implement a non-stable sort) – pilchard May 13 '22 at 22:18
  • 1
    You can also see: [Fast stable sorting algorithm implementation in javascript](https://stackoverflow.com/questions/1427608/fast-stable-sorting-algorithm-implementation-in-javascript) which assumes a non-stable built in sort (and results in your solution of sorting by property and original index) – pilchard May 13 '22 at 22:30
  • @pilchard whoa thanks a lot! I didn't even know that V8 docs were a thing. – opensorcio May 13 '22 at 22:33

0 Answers0