diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index c67324f2..f87ae777 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -32,7 +32,7 @@ interface DataGridColumn { } interface RowFilter { - field: keyof T + columnIdx: number value: any } @@ -78,29 +78,40 @@ function DataGrid(props: { } // Filtering - const fieldAlreadyFiltered = (field: keyof T): boolean => - filters.some((f) => f.field === field) + const fieldAlreadyFiltered = (columnIdx: number): boolean => + filters.some((f) => f.columnIdx === columnIdx) - const handleClickFilterCell = (field: keyof T, value: any) => { - if (fieldAlreadyFiltered(field)) { + const handleClickFilterCell = (columnIdx: number, value: any) => { + if (fieldAlreadyFiltered(columnIdx)) { return } - const newFilters = [...filters, { field: field, value: value }] + const newFilters = [...filters, { columnIdx: columnIdx, value: value }] setFilters(newFilters) } - const clearFilter = (field: keyof T): void => { - setFilters(filters.filter((f) => f.field !== field)) + const clearFilter = (columnIdx: number): void => { + setFilters(filters.filter((f) => f.columnIdx !== columnIdx)) } - const filteredRows = rows.filter((row) => { + const filteredRows = rows.filter((row, rowIdx) => { if (defaultFilter !== undefined && defaultFilter(row)) { return false } return filters.length === 0 ? true : filters.some((f) => { - return row[f.field] === f.value + if (columns.length <= f.columnIdx) { + console.log( + `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}` + ) + return true + } + const toCellValue = columns[f.columnIdx].toCellValue + if (toCellValue !== undefined) { + return toCellValue(rowIdx) === f.value + } + const field = columns[f.columnIdx].field + return row[field] === f.value }) }) @@ -147,18 +158,18 @@ function DataGrid(props: { {collapseBody ? : null} - {columns.map((column, index) => ( + {columns.map((column, columnIdx) => ( {column.sortable ? ( {column.label} {orderBy === column.field ? ( @@ -176,13 +187,13 @@ function DataGrid(props: { { - clearFilter(column.field) + clearFilter(columnIdx) }} > @@ -232,7 +243,7 @@ function DataGridRow(props: { row: T keyField: keyof T collapseBody?: (rowIndex: number) => React.ReactNode - handleClickFilterCell: (field: keyof T, value: any) => void + handleClickFilterCell: (columnIdx: number, value: any) => void }) { const { columns, @@ -274,7 +285,11 @@ function DataGridRow(props: { key={`${row[keyField]}:${column.field}:${columnIndex}`} padding={column.padding || "normal"} onClick={(e) => { - handleClickFilterCell(column.field, row[column.field]) + const value = + column.toCellValue !== undefined + ? column.toCellValue(rowIndex) + : row[column.field] + handleClickFilterCell(columnIndex, value) }} > {cellItem} diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index 08c5c08d..d442cdc7 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -605,13 +605,14 @@ export const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({ ) { studyDetail?.intersection_search_space.forEach((s) => { const sortable = s.distribution !== "CategoricalDistribution" + const filterable = s.distribution === "CategoricalDistribution" columns.push({ field: "params", label: `Param ${s.name}`, toCellValue: (i) => trials[i].params.find((p) => p.name === s.name)?.value || null, sortable: sortable, - filterable: false, // TODO(yoshinobc): Support filtering by categorical parameters + filterable: filterable, less: (firstEl, secondEl): number => { const firstVal = firstEl.params.find((p) => p.name === s.name)?.value const secondVal = secondEl.params.find(