Skip to content

Commit

Permalink
feat: Add a short-form for sort by encoding (e.g., sort: "x", `sort…
Browse files Browse the repository at this point in the history
…: "-x"`)

Fix #4933
  • Loading branch information
kanitw committed Jun 29, 2019
1 parent 79d7477 commit c21f90e
Show file tree
Hide file tree
Showing 18 changed files with 198 additions and 51 deletions.
2 changes: 2 additions & 0 deletions _data/link.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ SingleDefChannel:
SingleDefUnitChannel:
name: "Channel"
link: "encoding.html#channels"
AllSortString:
name: "String"

# Channel Definitions
NumericFieldDefWithCondition:
Expand Down
52 changes: 49 additions & 3 deletions build/vega-lite-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@
],
"type": "string"
},
"AllSortString": {
"anyOf": [
{
"$ref": "#/definitions/SortOrder"
},
{
"$ref": "#/definitions/SortByChannel"
},
{
"$ref": "#/definitions/SortByChannelDesc"
}
]
},
"AnyMark": {
"anyOf": [
{
Expand Down Expand Up @@ -10519,7 +10532,6 @@
"strokeOpacity",
"opacity",
"text",
"tooltip",
"href",
"key"
],
Expand Down Expand Up @@ -10688,7 +10700,7 @@
"$ref": "#/definitions/SortArray"
},
{
"$ref": "#/definitions/SortOrder"
"$ref": "#/definitions/AllSortString"
},
{
"$ref": "#/definitions/EncodingSortField"
Expand Down Expand Up @@ -10729,11 +10741,45 @@
}
]
},
"SortByChannel": {
"enum": [
"x",
"y",
"color",
"fill",
"stroke",
"strokeWidth",
"size",
"shape",
"fillOpacity",
"strokeOpacity",
"opacity",
"text"
],
"type": "string"
},
"SortByChannelDesc": {
"enum": [
"-x",
"-y",
"-color",
"-fill",
"-stroke",
"-strokeWidth",
"-size",
"-shape",
"-fillOpacity",
"-strokeOpacity",
"-opacity",
"-text"
],
"type": "string"
},
"SortByEncoding": {
"additionalProperties": false,
"properties": {
"encoding": {
"$ref": "#/definitions/SingleDefUnitChannel",
"$ref": "#/definitions/SortByChannel",
"description": "The [encoding channel](https://vega.github.io/vega-lite/docs/encoding.html#channels) to sort by (e.g., `\"x\"`, `\"y\"`)"
},
"order": {
Expand Down
2 changes: 1 addition & 1 deletion examples/specs/bar_aggregate_sort_by_encoding.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"field": "age",
"type": "ordinal",
"scale": {"rangeStep": 17},
"sort": {"encoding": "x"}
"sort": "-x"
},
"x": {
"aggregate": "sum",
Expand Down
4 changes: 2 additions & 2 deletions examples/specs/bar_count_minimap.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"type": "nominal",
"scale": {"domain": {"selection": "brush"}},
"axis": {"minExtent": 200, "title": null},
"sort": {"encoding": "x", "order": "descending"}
"sort": "-x"
},
"x": {
"aggregate": "count",
Expand All @@ -32,7 +32,7 @@
"y": {
"field": "Name",
"type": "nominal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"axis": null
},
"x": {
Expand Down
10 changes: 2 additions & 8 deletions examples/specs/interactive_dashboard_europe_pop.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,7 @@
"y": {
"field": "Country",
"type": "ordinal",
"sort": {
"encoding": "x",
"order": "descending"
}
"sort": "-x"
},
"x": {
"field": "Population_ages_15_64_of_total",
Expand All @@ -340,10 +337,7 @@
"y": {
"field": "Country",
"type": "ordinal",
"sort": {
"encoding": "x",
"order": "descending"
}
"sort": "-x"
},
"x": {
"field": "Population_ages_65_and_above_of_total",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
Expand All @@ -43,4 +43,4 @@
}
]
}
}
}
4 changes: 2 additions & 2 deletions examples/specs/normalized/trellis_barley_normalized.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
}
}
}
}
2 changes: 1 addition & 1 deletion examples/specs/trellis_barley.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
Expand Down
2 changes: 1 addition & 1 deletion examples/specs/trellis_barley_independent.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
Expand Down
2 changes: 1 addition & 1 deletion examples/specs/trellis_barley_layer_median.vl.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"y": {
"field": "variety",
"type": "ordinal",
"sort": {"encoding": "x", "order": "descending"},
"sort": "-x",
"scale": {"rangeStep": 12}
},
"color": {"field": "year", "type": "nominal"}
Expand Down
12 changes: 8 additions & 4 deletions site/docs/encoding/sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,18 @@ To order the data by the values' natural order in Javascript (e.g.,`"a"` < `"b"`

### Sort by Another Encoding Channel

To sort data by another encoding channel, the `sort` property can be an object with the `encoding` property:
To sort data by another encoding channel, the `sort` property can be an encoding channel name to sort by (e.g., `"x"` or `"y"`) with an optional minus prefix for descending sort (e.g., `"-x"` to sort by x-field, descending).

{% include table.html props="encoding,order" source="SortByEncoding" %}

For example, the following plot sorts the y-values by the x-values.
For example, the following plot sorts the y-values by the x-values (in descending order).

<div class="vl-example" data-name="bar_aggregate_sort_by_encoding"></div>

This is equivalent to using an object with the `encoding` and optional `"order"` property:

{% include table.html props="encoding,order" source="SortByEncoding" %}

For example, `"sort": "-x"` is equivalent to `"sort": {"encoding": "x", "order": "descending"}`.

### Sort by a Different Field

{:#sort-field}
Expand Down
5 changes: 2 additions & 3 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ const CHANNEL_INDEX = {

export const CHANNELS = flagKeys(CHANNEL_INDEX);

const {order: _o, detail: _d, ...SINGLE_DEF_CHANNEL_INDEX} = CHANNEL_INDEX;
const {order: _o1, detail: _d1, row: _r, column: _c, facet: _f, ...SINGLE_DEF_UNIT_CHANNEL_INDEX} = CHANNEL_INDEX;
const {order: _o, detail: _d, tooltip: _tt1, ...SINGLE_DEF_CHANNEL_INDEX} = CHANNEL_INDEX;
const {row: _r, column: _c, facet: _f, ...SINGLE_DEF_UNIT_CHANNEL_INDEX} = SINGLE_DEF_CHANNEL_INDEX;
/**
* Channels that cannot have an array of channelDef.
* model.fieldDef, getFieldDef only work for these channels.
Expand Down Expand Up @@ -183,7 +183,6 @@ export type SingleDefUnitChannel =
| 'strokeOpacity'
| 'opacity'
| 'text'
| 'tooltip'
| 'href'
| 'key';

Expand Down
3 changes: 2 additions & 1 deletion src/channeldef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ export interface SortableFieldDef<
*
* For discrete fields, `sort` can be one of the following:
* - `"ascending"` or `"descending"` -- for sorting by the values' natural order in Javascript.
* - [A sort-by-encoding definition](https://vega.github.io/vega-lite/docs/sort.html#sort-by-encoding) for sorting by another encoding channel. (This type of sort definition is not available for `row` and `column` channels.)
* - [A string indicating an encoding channel name to sort by](https://vega.github.io/vega-lite/docs/sort.html#sort-by-encoding) (e.g., `"x"` or `"y"`) with an optional minus prefix for descending sort (e.g., `"-x"` to sort by x-field, descending).
* - [A sort-by-encoding definition](https://vega.github.io/vega-lite/docs/sort.html#sort-by-encoding) for sorting by another encoding channel. For example, `"sort": "-x"` is equivalent to `"sort": {"encoding": "x", "order": "descending"}`. (This type of sort definition is not available for `row` and `column` channels.)
* - [A sort field definition](https://vega.github.io/vega-lite/docs/sort.html#sort-field) for sorting by another field.
* - [An array specifying the field values in preferred order](https://vega.github.io/vega-lite/docs/sort.html#sort-array). In this case, the sort order will obey the values in the array, followed by any unspecified values in their original order. For discrete time field, values in the sort array can be [date-time definition objects](types#datetime). In addition, for time units `"month"` and `"day"`, the values can be the month or day names (case insensitive) or their 3-letter initials (e.g., `"Mon"`, `"Tue"`).
* - `null` indicating no sort.
Expand Down
48 changes: 32 additions & 16 deletions src/compile/scale/domain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {AggregateOp} from 'vega';
import {AggregateOp, Order} from 'vega';
import {isObject, isString} from 'vega-util';
import {SHARED_DOMAIN_OP_INDEX} from '../../aggregate';
import {isBinning} from '../../bin';
import {isScaleChannel, ScaleChannel} from '../../channel';
import {isChannel, isScaleChannel, isSingleDefUnitChannel, ScaleChannel, SingleDefUnitChannel} from '../../channel';
import {binRequiresRange, ScaleFieldDef, TypedFieldDef, valueExpr, vgField} from '../../channeldef';
import {MAIN, RAW} from '../../data';
import {DateTime} from '../../datetime';
Expand Down Expand Up @@ -329,6 +329,17 @@ function normalizeSortField(sort: EncodingSortField<string>, isStacked: boolean)
};
}

function sortByEncoding(encoding: SingleDefUnitChannel, order: Order, model: UnitModel) {
const isStacked = model.stack !== null;
const {aggregate, field} = model.fieldDef(encoding);
const sortField: EncodingSortField<string> = {
op: aggregate as AggregateOp, // Once we decouple aggregate from aggregate op we won't have to cast here
field,
...(order ? {order} : {})
};
return normalizeSortField(sortField, isStacked);
}

export function domainSort(
model: UnitModel,
channel: ScaleChannel,
Expand Down Expand Up @@ -357,20 +368,25 @@ export function domainSort(
return normalizeSortField(sort, isStacked);
} else if (isSortByEncoding(sort)) {
const {encoding, order} = sort;
const {aggregate, field} = model.fieldDef(encoding);
const sortField: EncodingSortField<string> = {
op: aggregate as AggregateOp, // Once we decouple aggregate from aggregate op we won't have to cast here
field,
order
};
return normalizeSortField(sortField, isStacked);
} else if (sort === 'descending') {
return {
op: 'min',
field: model.vgField(channel),
order: 'descending'
};
} else if (util.contains(['ascending', undefined /* default =ascending*/], sort)) {
return sortByEncoding(encoding, order, model);
} else if (isString(sort)) {
const sub = sort.substr(1);
if (isChannel(sort)) {
// e.g., "x", "y"
return sortByEncoding(sort, undefined, model);
} else if (isSingleDefUnitChannel(sub)) {
// e.g., "-x", "-y"
return sortByEncoding(sub, 'descending', model);
} else if (sort === 'descending') {
return {
op: 'min',
field: model.vgField(channel),
order: 'descending'
};
}
}

if (util.contains(['ascending', undefined /* default =ascending*/], sort)) {
return true;
}

Expand Down
35 changes: 32 additions & 3 deletions src/sort.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {AggregateOp} from 'vega';
import {isArray} from 'vega-util';
import {SingleDefUnitChannel} from './channel';
import {FieldName} from './channeldef';
import {DateTime} from './datetime';

Expand Down Expand Up @@ -60,7 +59,7 @@ export interface SortByEncoding {
/**
* The [encoding channel](https://vega.github.io/vega-lite/docs/encoding.html#channels) to sort by (e.g., `"x"`, `"y"`)
*/
encoding: SingleDefUnitChannel;
encoding: SortByChannel;

/**
* The sort order. One of `"ascending"` (default), `"descending"`, or `null` (no not sort).
Expand All @@ -70,7 +69,37 @@ export interface SortByEncoding {

export type SortArray = number[] | string[] | boolean[] | DateTime[];

export type Sort<F> = SortArray | SortOrder | EncodingSortField<F> | SortByEncoding | null;
export type SortByChannel =
| 'x'
| 'y'
| 'color'
| 'fill'
| 'stroke'
| 'strokeWidth'
| 'size'
| 'shape'
| 'fillOpacity'
| 'strokeOpacity'
| 'opacity'
| 'text';

export type SortByChannelDesc =
| '-x'
| '-y'
| '-color'
| '-fill'
| '-stroke'
| '-strokeWidth'
| '-size'
| '-shape'
| '-fillOpacity'
| '-strokeOpacity'
| '-opacity'
| '-text';

export type AllSortString = SortOrder | SortByChannel | SortByChannelDesc;

export type Sort<F> = SortArray | AllSortString | EncodingSortField<F> | SortByEncoding | null;

export function isSortByEncoding<F>(sort: Sort<F>): sort is SortByEncoding {
return !!sort && !!sort['encoding'];
Expand Down
2 changes: 1 addition & 1 deletion test/channel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('channel', () => {

describe('SINGLE_DEF_CHANNELS', () => {
it('should be CHANNELS without detail and order', () => {
expect(SINGLE_DEF_CHANNELS).toEqual(without(CHANNELS, ['detail', 'order']));
expect(SINGLE_DEF_CHANNELS).toEqual(without(CHANNELS, ['detail', 'order', 'tooltip']));
});
});

Expand Down
Loading

0 comments on commit c21f90e

Please sign in to comment.