Skip to content

Commit d0bf9b4

Browse files
committed
descending shorthand
1 parent bb51b4e commit d0bf9b4

25 files changed

+48
-49
lines changed

docs/features/facets.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Plot.plot({
4242
y: "variety",
4343
fy: "site",
4444
stroke: "year",
45-
sort: {y: "x", fy: "x", reduce: "median", reverse: true}
45+
sort: {y: "-x", fy: "-x", reduce: "median"}
4646
})
4747
]
4848
})
@@ -81,7 +81,7 @@ Plot.plot({
8181
fy: "site",
8282
stroke: "yield",
8383
strokeWidth: 2,
84-
sort: {y: "x1", fy: "x1", reduce: "median", reverse: true}
84+
sort: {y: "-x1", fy: "-x1", reduce: "median"}
8585
}))
8686
]
8787
})

docs/features/scales.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -977,10 +977,10 @@ Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reduce: "max"}}
977977

978978
Generally speaking, a reducer only needs to be specified when there are multiple secondary values for a given primary value. See the [group transform](../transforms/group.md) for the list of supported reducers.
979979

980-
For descending rather than ascending order, use the *reverse* option:
980+
For descending rather than ascending order, use the *reverse* option, or *-name* when referring to a channel:
981981

982982
```js
983-
Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}})
983+
Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "-y"}})
984984
```
985985

986986
An additional *limit* option truncates the domain to the first *n* values after sorting. If *limit* is negative, the last *n* values are used instead. Hence, a positive *limit* with *reverse* = true will return the top *n* values in descending order. If *limit* is an array [*lo*, *hi*], the *i*th values with *lo**i* < *hi* will be selected. (Note that like the [basic filter transform](../transforms/filter.md), limiting the *x* domain here does not affect the computation of the *y* domain, which is computed independently without respect to filtering.)

docs/marks/bar.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Ordinal domains are sorted naturally (alphabetically) by default. Either set the
4141

4242
:::plot https://observablehq.com/@observablehq/plot-vertical-bars
4343
```js
44-
Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "y", reverse: true}}).plot()
44+
Plot.barY(alphabet, {x: "letter", y: "frequency", sort: {x: "-y"}}).plot()
4545
```
4646
:::
4747

docs/transforms/group.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Plot.plot({
5252
x: {label: null, tickRotate: 90},
5353
y: {grid: true},
5454
marks: [
55-
Plot.barY(olympians, Plot.groupX({y: "count"}, {x: "sport", sort: {x: "y", reverse: true}})),
55+
Plot.barY(olympians, Plot.groupX({y: "count"}, {x: "sport", sort: {x: "-y"}})),
5656
Plot.ruleY([0])
5757
]
5858
})

docs/transforms/sort.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Plot.plot({
4949
fill: "currentColor",
5050
stroke: "var(--vp-c-bg)",
5151
strokeWidth: 1,
52-
sort: sorted ? {channel: "r", order: "descending"} : null
52+
sort: sorted ? {channel: "-r"} : null
5353
}))
5454
]
5555
})
@@ -134,7 +134,7 @@ Sorts the data by the specified *order*, which is one of:
134134
- a field name
135135
- a {*channel*, *order*} object
136136

137-
In the object case, the **channel** option specifies the name of the channel, while the **order** option specifies *ascending* (the default) or *descending* order. For example, `sort: {channel: "r", order: "descending"}` will sort by descending radius (**r**).
137+
In the object case, the **channel** option specifies the name of the channel, while the **order** option specifies *ascending* (the default) or *descending* order. You can also use the shorthand *-name* to sort by descending order of the channel with the given *name*. For example, `sort: {channel: "-r"}` will sort by descending radius (**r**).
138138

139139
In the function case, if the sort function does not take exactly one argument, it is interpreted as a comparator function; otherwise it is interpreted as an accessor function.
140140

src/channel.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ export type ChannelValueBinSpec = ChannelValue | ({value: ChannelValue} & BinOpt
161161
*/
162162
export type ChannelValueDenseBinSpec = ChannelValue | ({value: ChannelValue; scale?: Channel["scale"]} & Omit<BinOptions, "interval">); // prettier-ignore
163163

164+
/** A channel name, or an implied one for domain sorting. */
165+
type ChannelDomainName = ChannelName | "data" | "width" | "height";
166+
164167
/**
165168
* The available inputs for imputing scale domains. In addition to a named
166169
* channel, an input may be specified as:
@@ -176,7 +179,7 @@ export type ChannelValueDenseBinSpec = ChannelValue | ({value: ChannelValue; sca
176179
* custom **reduce** function, as when the built-in single-channel reducers are
177180
* insufficient.
178181
*/
179-
export type ChannelDomainValue = ChannelName | "data" | "width" | "height" | null;
182+
export type ChannelDomainValue = ChannelDomainName | `-${ChannelDomainName}` | null;
180183

181184
/** Options for imputing scale domains from channel values. */
182185
export interface ChannelDomainOptions {

src/channel.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ export function channelDomain(data, facets, channels, facetChannels, options) {
8282
for (const x in options) {
8383
if (!registry.has(x)) continue; // ignore unknown scale keys (including generic options)
8484
let {value: y, reverse = defaultReverse, reduce = defaultReduce, limit = defaultLimit} = maybeValue(options[x]);
85+
const negate = y?.startsWith("-");
86+
if (negate) y = y.slice(1);
8587
if (reverse === undefined) reverse = y === "width" || y === "height"; // default to descending for lengths
88+
if (negate) reverse = !reverse;
8689
if (reduce == null || reduce === false) continue; // disabled reducer
8790
const X = x === "fx" || x === "fy" ? reindexFacetChannel(facets, facetChannels[x]) : findScaleChannel(channels, x);
8891
if (!X) throw new Error(`missing channel for scale: ${x}`);

src/mark.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export interface MarkOptions {
102102
* with a *value* object and per-scale options:
103103
*
104104
* ```js
105-
* sort: {y: {value: "x", reverse: true}}
105+
* sort: {y: {value: "-x"}}
106106
* ```
107107
*
108108
* When sorting the mark’s index, the **sort** option is instead one of:

src/marks/dot.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ const defaults = {
2323
};
2424

2525
export function withDefaultSort(options) {
26-
return options.sort === undefined && options.reverse === undefined
27-
? sort({channel: "r", order: "descending"}, options)
28-
: options;
26+
return options.sort === undefined && options.reverse === undefined ? sort({channel: "-r"}, options) : options;
2927
}
3028

3129
export class Dot extends Mark {

src/transforms/basic.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ export type SortOrder =
149149
| CompareFunction
150150
| ChannelValue
151151
| {value?: ChannelValue; order?: CompareFunction | "ascending" | "descending"}
152-
| {channel?: ChannelName; order?: CompareFunction | "ascending" | "descending"};
152+
| {channel?: ChannelName | `-${ChannelName}`; order?: CompareFunction | "ascending" | "descending"};
153153

154154
/**
155155
* Applies a transform to *options* to sort the mark’s index by the specified

0 commit comments

Comments
 (0)