inputs icon indicating copy to clipboard operation
inputs copied to clipboard

Stacked sorts on Input.table()

Open hellonearthis opened this issue 3 years ago • 3 comments

Having the ability to stack sorts on an Input.table(dates,{sort:'date',sort:'month'}) would be useful.
Dates is an unordered list.

For example. I could sort by date and then month to get put my data into chronological order.
It's like clicking on the dates column and then the months column.

image

I thought that issue 114 was similar but it would not give the same results as a cascading sort.

hellonearthis avatar Jun 07 '22 06:06 hellonearthis

The syntax would have to be slightly different—maybe with:

  • an array: sort: ["date", "month"]
  • a comparator: sort:(a, b) => d3.ascending(a.date, b.date) || d3.ascending(a.month, b.month)
  • or an accessor: sort: (d) => '${d.month}-${d.date}'

Fil avatar Jun 07 '22 06:06 Fil

Sorting by additional columns would probably require some modifier key. Otherwise the sorts would only continue to stack up, with no apparent way to set a new primary sort column.

A few other things to consider:

  • Should stacking be limited to two columns?
  • Should a secondary sort have a different visual indicator?
  • Comparator definitions should follow the pattern currently used for other options. That is, an object with per-column comparator callbacks. See also https://github.com/observablehq/inputs/issues/116 which suggests an alternative option structure to improve reusability.

mootari avatar Jun 08 '22 08:06 mootari

A possible API could look something like this:

const PRIORITIES = ['low', 'medium', 'high']

const data = [
    { title: 'b', chinese: '安', date: new Date('2023-12-01T00:00:00Z'), priority: 'medium' },
    { title: 'a', chinese: '测', date: new Date('2023-12-05T00:00:00Z'), priority: 'low' },
    { title: 'c', chinese: '比', date: new Date('2023-12-02T00:00:00Z'), priority: 'high' },
]

Inputs.table(data, {
    columns: ['title', 'chinese', 'date', 'priority'],
    sort: [
        // needs to be an array not an obj at top level because ordering is important
        // (i.e. if result of first comparator is non-zero, other comparators aren't evaluated)
        { column: 'priority', direction: 'desc' },
        { column: 'date', direction: 'asc' },
    ],
    comparators: {
        title: new Intl.Collator('en-US').compare,
        chinese: new Intl.Collator('zh-CN').compare,
        date: (a, b) => d3.ascending(a.date, b.date),
        priority: (a, b) => PRIORITIES.indexOf(a.priority) - PRIORITIES.indexOf(b.priority),
    },
})

In this example, the default sorting would be by priority (from high to low), and within the same priority, by date (from earlier to later).

User interaction for nuking the stacked sort order and setting a new "simple" sort order (i.e. based on a single column using its comparator, or default generic comparator if none set) would be the same as the current one, i.e. clicking that column heading, alternating between asc and desc orderings. Regarding users setting their own stacked sort orders, I'm not sure, but at a minimum it'd be nice to have a user action for "reset to default sort order" (whether simple, stacked, or completely unsorted).

lionel-rowe avatar Dec 08 '23 05:12 lionel-rowe