T
TanStack2y ago
exotic-emerald

Custom sorting fn and undefined values

Hi, I am trying to implement a custom sorting function on categorical values but I have not succeeded so far. I've put together the simple codesandbox below, where I'd like the undefined values to always show up at the bottom of the table, be it ascending or descending on the status column. Am I missing something in my implementation? Looking forward to any pointers, I've really spent way to much time on this issue... Thanks https://codesandbox.io/p/sandbox/purple-morning-44jlhw?file=%2Fsrc%2Fmain.tsx%3A78%2C21
12 Replies
exotic-emerald
exotic-emeraldOP2y ago
Discord
Discord - A New Way to Chat with Friends & Communities
Discord is the easiest way to communicate over voice, video, and text. Chat, hang out, and stay close with your friends and communities.
exotic-emerald
exotic-emeraldOP2y ago
Seems like I'm not the only one having issues with this? see last comments in the thread https://github.com/TanStack/table/issues/4113
GitHub
Undefined being included in sorting when setting sortUndefined to f...
Describe the bug Setting up a table of data with the latest table release. When having the following: { accessorFn: ({ foo }) => foo, // undefined sortUndefined: false, cell({ row }) { return ro...
exotic-emerald
exotic-emeraldOP2y ago
Solved it by using the table ref. It works, but I really think that exposing the sort direction to SortingFn instances would make this cleaner.
export const categoricalSortingFn = <RowType extends Record<string, unknown>>(
table: Table<RowType> | null,
values: string[],
): SortingFn<RowType> => {
return (rowA: Row<RowType>, rowB: Row<RowType>, columnId: string) => {
const desc = table?.getState().sorting.find((x) => x.id === columnId)?.desc;
const sortedArray = desc ? [...values, undefined] : [undefined, ...values];
const valA = rowA.getValue<string | undefined>(columnId);
const valB = rowB.getValue<string | undefined>(columnId);
const iA = sortedArray.indexOf(valA);
const iB = sortedArray.indexOf(valB);
const order = iA < iB ? 1 : -1;
return order;
};
};
export const categoricalSortingFn = <RowType extends Record<string, unknown>>(
table: Table<RowType> | null,
values: string[],
): SortingFn<RowType> => {
return (rowA: Row<RowType>, rowB: Row<RowType>, columnId: string) => {
const desc = table?.getState().sorting.find((x) => x.id === columnId)?.desc;
const sortedArray = desc ? [...values, undefined] : [undefined, ...values];
const valA = rowA.getValue<string | undefined>(columnId);
const valB = rowB.getValue<string | undefined>(columnId);
const iA = sortedArray.indexOf(valA);
const iB = sortedArray.indexOf(valB);
const order = iA < iB ? 1 : -1;
return order;
};
};
exotic-emerald
exotic-emeraldOP2y ago
Discussed in this github issue as well https://github.com/TanStack/table/discussions/4920
GitHub
[Feature Request] Add SortDirection parameter to custom `SortingF...
We have cases where we want certain values to always be sorted at the end despite of sort direction. This can be achieved by passing the column sort direction to a custom sorting function, where we...
adverse-sapphire
adverse-sapphire2y ago
This would be helpful to me too.
rival-black
rival-black17mo ago
I have used the above custom-sorting function and assigned it as "sortingFn: categoricalSortingFn" for each column. Will the header?.column?.toggleSorting() method on each header will refer to categoricalSortingFn? I didn't see an effect doing so. My requirement is to pinned rows to be excluded from being sorted and always keep them at the top. I need to use the above categoricalSortingFn to write logic for pinned-rows to be excluded. Please help.
optimistic-gold
optimistic-gold17mo ago
Why not use the actual row pinning feature instead of relying on custom sorting behavior?
optimistic-gold
optimistic-gold17mo ago
rare-sapphire
rare-sapphire13mo ago
@Tanner Linsley any chance we can get an update on this? Would go a long way to solve a similar issue I'm facing
deep-jade
deep-jade13mo ago
@KevinVandy is more up to date with the situation than I am right now. I'm neck-deep in Router/Start.
optimistic-gold
optimistic-gold13mo ago
This conversation has a few topics in it. What would be your question?
rare-sapphire
rare-sapphire13mo ago
It would be great to have access to the sort direction of the column in sortingFn ; i've got a case where based on a field i'm not displaying/using in the grid, I want to sort certain rows to the top/bottom regardless of direction (think if I had a list of Hondas and Toyotas, and wanted to sort by gas mileage, but wanted all Hondas to appear in order before the Toyotas started showing up) It's also something i'd be down to create the PR for, but wanted to make sure it's an update people would be willing to pull in before I spend time on it this is the current workaround that I have going:
const sortByManufacturerThenField = (rowA: Row<any>, rowB: Row<any>, columnId: string) => {
let isDescMultiplier = 1;
const sortState = rowA._getAllCellsByColumnId()[columnId].getContext().table.getState().sorting;
if (sortState && sortState.length) {
isDescMultiplier = sortState[0].desc ? -1 : 1;
}

if (rowA.original.manufacturerId === rowB.original.manufacturerId) {
return rowA.getValue(columnId) > rowB.getValue(columnId) ? 1 : -1;
}
/**
* base case is that Honda > Toyota (rowA > rowB ? 1 : -1)
* We need to invert the sort order if the column is being sorted `desc` (rowA > rowB ? 1 : -1)
*
* ^ this all only applies to the sort-by-manufacturer secondary sort; all of the asc/desc logic and inversion logic is
* correctly applied to the actual sort value of the columns.
*/
return rowA.original.manufacturerId > rowB.original.manufacturerId
? isDescMultiplier * 1
: isDescMultiplier * -1;
};
const sortByManufacturerThenField = (rowA: Row<any>, rowB: Row<any>, columnId: string) => {
let isDescMultiplier = 1;
const sortState = rowA._getAllCellsByColumnId()[columnId].getContext().table.getState().sorting;
if (sortState && sortState.length) {
isDescMultiplier = sortState[0].desc ? -1 : 1;
}

if (rowA.original.manufacturerId === rowB.original.manufacturerId) {
return rowA.getValue(columnId) > rowB.getValue(columnId) ? 1 : -1;
}
/**
* base case is that Honda > Toyota (rowA > rowB ? 1 : -1)
* We need to invert the sort order if the column is being sorted `desc` (rowA > rowB ? 1 : -1)
*
* ^ this all only applies to the sort-by-manufacturer secondary sort; all of the asc/desc logic and inversion logic is
* correctly applied to the actual sort value of the columns.
*/
return rowA.original.manufacturerId > rowB.original.manufacturerId
? isDescMultiplier * 1
: isDescMultiplier * -1;
};
ideally I'd be able to skip the chunk of code where I query the table state to figure out if the column is being sorted desc/asc and just use a sortDirection flag that's passed to the sortingFn definition

Did you find this page helpful?