diff --git a/optuna_dashboard/static/components/DataGrid.tsx b/optuna_dashboard/static/components/DataGrid.tsx index 677f5670..f62d76e7 100644 --- a/optuna_dashboard/static/components/DataGrid.tsx +++ b/optuna_dashboard/static/components/DataGrid.tsx @@ -45,10 +45,10 @@ const useStyles = makeStyles((theme: Theme) => ) interface DataGridColumn { - field: keyof T // TODO(c-bata): remove this or optional? - // TODO(c-bata): add comparator(or less function) field? see https://golang.org/pkg/sort/#Slice + field: keyof T label: string sortable?: boolean + less?: (i: number, j: number) => number filterable?: boolean toCellValue?: (rowIndex: number) => string | React.ReactNode padding?: "default" | "checkbox" | "none" @@ -132,7 +132,8 @@ function DataGrid(props: { setOrder(isAsc ? "desc" : "asc") setOrderBy(columnId) } - const sortedRows = stableSort(filteredRows, getComparator(order, columns, orderBy)) + const lessFunc = columns[orderBy].less + const sortedRows = stableSort(filteredRows, getComparator(order, columns, orderBy), order, lessFunc) const currentPageRows = rowsPerPage > 0 ? sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) @@ -325,11 +326,21 @@ function descendingComparator( return 0 } -function stableSort(array: T[], comparator: (a: T, b: T) => number) { +function stableSort( + array: T[], + comparator: (a: T, b: T) => number, + order: Order, + less?: (i: number, j: number) => number +) { const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) stabilizedThis.sort((a, b) => { - const order = comparator(a[0], b[0]) - if (order !== 0) return order + if (less) { + const result = order == "asc" ? -less(a[1], b[1]) : less(a[1], b[1]) + if (result !== 0) return result + } else { + const result = comparator(a[0], b[0]) + if (result !== 0) return result + } return a[1] - b[1] }) return stabilizedThis.map((el) => el[0]) diff --git a/optuna_dashboard/static/components/StudyDetail.tsx b/optuna_dashboard/static/components/StudyDetail.tsx index e32450e6..661c02c9 100644 --- a/optuna_dashboard/static/components/StudyDetail.tsx +++ b/optuna_dashboard/static/components/StudyDetail.tsx @@ -225,6 +225,16 @@ const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({ field: "values", label: "Value", sortable: true, + less: (i, j): number => { + if (trials[i].values?.[0] === trials[j].values?.[0]) { + return 0 + } else if (trials[i].values === undefined) { + return -1 + } else if (trials[j].values === undefined) { + return 1 + } + return trials[i].values![0] < trials[j].values![0] ? 1 : -1 + }, toCellValue: (i) => trials[i].values?.[0] || null, }) } else { @@ -234,6 +244,16 @@ const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({ field: "values", label: `Objective ${objectiveId}`, sortable: true, + less: (i, j): number => { + if (trials[i].values?.[objectiveId] === trials[j].values?.[objectiveId]) { + return 0 + } else if (trials[i].values === undefined) { + return -1 + } else if (trials[j].values === undefined) { + return 1 + } + return trials[i].values![objectiveId] < trials[j].values![objectiveId] ? 1 : -1 + }, toCellValue: (i) => trials[i].values?.[objectiveId] || null, })) columns.push(...objectiveColumns)