Skip to content

Commit 5c5acb7

Browse files
mbostockFil
andauthored
descending order (#1607)
* descending order * fix null order; add tests * Update src/channel.d.ts Co-authored-by: Philippe Rivière <[email protected]> --------- Co-authored-by: Philippe Rivière <[email protected]>
1 parent 0490ba6 commit 5c5acb7

21 files changed

+9895
-37
lines changed

src/channel.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {Interval} from "./interval.js";
22
import type {Reducer} from "./reducer.js";
33
import type {ScaleName, ScaleType} from "./scales.js";
4+
import type {CompareFunction} from "./transforms/basic.js";
45
import type {BinOptions} from "./transforms/bin.js";
56

67
/** Lazily-constructed channel values derived from data. */
@@ -192,7 +193,10 @@ export interface ChannelDomainOptions {
192193
*/
193194
reduce?: Reducer | boolean | null;
194195

195-
/** If true, use descending instead of ascending order. */
196+
/** How to order reduced values. */
197+
order?: CompareFunction | "ascending" | "descending" | null;
198+
199+
/** If true, reverse the order after sorting. */
196200
reverse?: boolean;
197201

198202
/**

src/channel.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {InternSet, rollup, sort} from "d3";
1+
import {InternSet, rollups} from "d3";
22
import {ascendingDefined, descendingDefined} from "./defined.js";
33
import {first, isColor, isEvery, isIterable, isOpacity, labelof, map, maybeValue, range, valueof} from "./options.js";
44
import {registry} from "./scales/index.js";
@@ -78,11 +78,11 @@ export function inferChannelScale(name, channel) {
7878
// computed; i.e., if the scale’s domain is set explicitly, that takes priority
7979
// over the sort option, and we don’t need to do additional work.
8080
export function channelDomain(data, facets, channels, facetChannels, options) {
81-
const {reverse: defaultReverse, reduce: defaultReduce = true, limit: defaultLimit} = options;
81+
const {order: defaultOrder, reverse: defaultReverse, reduce: defaultReduce = true, limit: defaultLimit} = options;
8282
for (const x in options) {
8383
if (!registry.has(x)) continue; // ignore unknown scale keys (including generic options)
84-
let {value: y, reverse = defaultReverse, reduce = defaultReduce, limit = defaultLimit} = maybeValue(options[x]);
85-
if (reverse === undefined) reverse = y === "width" || y === "height"; // default to descending for lengths
84+
let {value: y, order = defaultOrder, reverse = defaultReverse, reduce = defaultReduce, limit = defaultLimit} = maybeValue(options[x]); // prettier-ignore
85+
order = order === undefined ? y === "width" || y === "height" ? descendingGroup : ascendingGroup : maybeOrder(order); // prettier-ignore
8686
if (reduce == null || reduce === false) continue; // disabled reducer
8787
const X = x === "fx" || x === "fy" ? reindexFacetChannel(facets, facetChannels[x]) : findScaleChannel(channels, x);
8888
if (!X) throw new Error(`missing channel for scale: ${x}`);
@@ -106,12 +106,13 @@ export function channelDomain(data, facets, channels, facetChannels, options) {
106106
: values(channels, y, y === "y" ? "y2" : y === "x" ? "x2" : undefined);
107107
const reducer = maybeReduce(reduce === true ? "max" : reduce, YV);
108108
X.domain = () => {
109-
let domain = rollup(
109+
let domain = rollups(
110110
range(XV),
111111
(I) => reducer.reduceIndex(I, YV),
112112
(i) => XV[i]
113113
);
114-
domain = sort(domain, reverse ? descendingGroup : ascendingGroup);
114+
if (order) domain.sort(order);
115+
if (reverse) domain.reverse();
115116
if (lo !== 0 || hi !== Infinity) domain = domain.slice(lo, hi);
116117
return domain.map(first);
117118
};
@@ -154,6 +155,17 @@ function values(channels, name, alias) {
154155
throw new Error(`missing channel: ${name}`);
155156
}
156157

158+
function maybeOrder(order) {
159+
if (order == null || typeof order === "function") return order;
160+
switch (`${order}`.toLowerCase()) {
161+
case "ascending":
162+
return ascendingGroup;
163+
case "descending":
164+
return descendingGroup;
165+
}
166+
throw new Error(`invalid order: ${order}`);
167+
}
168+
157169
function ascendingGroup([ak, av], [bk, bv]) {
158170
return ascendingDefined(av, bv) || ascendingDefined(ak, bk);
159171
}
Lines changed: 104 additions & 0 deletions
Loading
Lines changed: 104 additions & 0 deletions
Loading

test/output/athletesNationality.svg renamed to test/output/channelDomainComparator.svg

Lines changed: 2 additions & 7 deletions
Loading

0 commit comments

Comments
 (0)