From 4a797a45f6348b4feedf3a8ed1a2a7823ec8d1b3 Mon Sep 17 00:00:00 2001 From: Christopher Pruijsen Date: Sun, 13 Sep 2026 09:21:21 +0000 Subject: [PATCH] groupSort: use ascendingDefined ascending returns NaN when either side is null or NaN, and NaN is falsy, so `ascending(av, bv) || ascending(ak, bk)` fell through to the key comparison instead of ranking the non-orderable value. A group whose reduced value is null sorted by key among the real values rather than last: [z:2, a:null, m:1] gave [a, m, z] rather than [m, z, a]. ascendingDefined returns 1 or -1 there, so the || no longer swallows it. The key comparison is switched too, as the issue asks. That one is not a behaviour change: sort wraps any comparator in compareDefined, whose fallback already ranks a NaN result last, so keys were ordered correctly through the wrapper. It is switched so the comparator is right on its own rather than by rescue. Fixes #273 --- src/groupSort.js | 7 +++---- test/groupSort-test.js | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/groupSort.js b/src/groupSort.js index 2d56ccf0..63aba28f 100644 --- a/src/groupSort.js +++ b/src/groupSort.js @@ -1,10 +1,9 @@ -import ascending from "./ascending.js"; import group, {rollup} from "./group.js"; -import sort from "./sort.js"; +import sort, {ascendingDefined} from "./sort.js"; export default function groupSort(values, reduce, key) { return (reduce.length !== 2 - ? sort(rollup(values, reduce, key), (([ak, av], [bk, bv]) => ascending(av, bv) || ascending(ak, bk))) - : sort(group(values, key), (([ak, av], [bk, bv]) => reduce(av, bv) || ascending(ak, bk)))) + ? sort(rollup(values, reduce, key), (([ak, av], [bk, bv]) => ascendingDefined(av, bv) || ascendingDefined(ak, bk))) + : sort(group(values, key), (([ak, av], [bk, bv]) => reduce(av, bv) || ascendingDefined(ak, bk)))) .map(([key]) => key); } diff --git a/test/groupSort-test.js b/test/groupSort-test.js index 2d7d76b3..6260d4fc 100644 --- a/test/groupSort-test.js +++ b/test/groupSort-test.js @@ -31,6 +31,11 @@ it("groupSort(data, reduce, key) returns sorted keys when reduce is an accessor" ); }); +it("groupSort(data, reduce, key) puts non-orderable reduced values last", () => { + const data = [{key: "z", value: 2}, {key: "a", value: null}, {key: "m", value: 1}]; + assert.deepStrictEqual(groupSort(data, g => g[0].value, d => d.key), ["m", "z", "a"]); +}); + it("groupSort(data, reduce, key) returns sorted keys when reduce is a comparator", () => { assert.deepStrictEqual( groupSort(barley, (a, b) => ascending(median(a, d => d.yield), median(b, d => d.yield)), d => d.variety),