mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-19 10:58:10 +08:00
correctly handle selection of trunctated categories (#781)
This commit is contained in:
@@ -27,10 +27,10 @@ class Category extends React.Component {
|
||||
const cat = categoricalSelection[metadataField];
|
||||
const categoryCount = {
|
||||
// total number of categories in this dimension
|
||||
totalCatCount: cat.numCategories,
|
||||
totalCatCount: cat.numCategoryValues,
|
||||
// number of selected options in this category
|
||||
selectedCatCount: _.reduce(
|
||||
cat.categorySelected,
|
||||
cat.categoryValueSelected,
|
||||
(res, cond) => (cond ? res + 1 : res),
|
||||
0
|
||||
)
|
||||
@@ -91,7 +91,7 @@ class Category extends React.Component {
|
||||
const { categoricalSelection, metadataField } = this.props;
|
||||
|
||||
const cat = categoricalSelection[metadataField];
|
||||
const optTuples = sortedCategoryValues([...cat.categoryIndices]);
|
||||
const optTuples = sortedCategoryValues([...cat.categoryValueIndices]);
|
||||
return _.map(optTuples, (tuple, i) => (
|
||||
<Value
|
||||
optTuples={optTuples}
|
||||
|
||||
@@ -46,8 +46,8 @@ class CategoryValue extends React.Component {
|
||||
if (!categoricalSelection) return null;
|
||||
|
||||
const category = categoricalSelection[metadataField];
|
||||
const selected = category.categorySelected[categoryIndex];
|
||||
const count = category.categoryCounts[categoryIndex];
|
||||
const selected = category.categoryValueSelected[categoryIndex];
|
||||
const count = category.categoryValueCounts[categoryIndex];
|
||||
const value = category.categoryValues[categoryIndex];
|
||||
const displayString = String(
|
||||
category.categoryValues[categoryIndex]
|
||||
|
||||
@@ -30,15 +30,15 @@ const CategoricalSelection = (
|
||||
/*
|
||||
Set the specific category in this field to false
|
||||
*/
|
||||
const newCategorySelected = Array.from(
|
||||
state[action.metadataField].categorySelected
|
||||
const newCategoryValueSelected = Array.from(
|
||||
state[action.metadataField].categoryValueSelected
|
||||
);
|
||||
newCategorySelected[action.categoryIndex] = true;
|
||||
newCategoryValueSelected[action.categoryIndex] = true;
|
||||
const newCategoricalSelection = {
|
||||
...state,
|
||||
[action.metadataField]: {
|
||||
...state[action.metadataField],
|
||||
categorySelected: newCategorySelected
|
||||
categoryValueSelected: newCategoryValueSelected
|
||||
}
|
||||
};
|
||||
return newCategoricalSelection;
|
||||
@@ -48,15 +48,15 @@ const CategoricalSelection = (
|
||||
/*
|
||||
Set the specific category in this field to false
|
||||
*/
|
||||
const newCategorySelected = Array.from(
|
||||
state[action.metadataField].categorySelected
|
||||
const newCategoryValueSelected = Array.from(
|
||||
state[action.metadataField].categoryValueSelected
|
||||
);
|
||||
newCategorySelected[action.categoryIndex] = false;
|
||||
newCategoryValueSelected[action.categoryIndex] = false;
|
||||
const newCategoricalSelection = {
|
||||
...state,
|
||||
[action.metadataField]: {
|
||||
...state[action.metadataField],
|
||||
categorySelected: newCategorySelected
|
||||
categoryValueSelected: newCategoryValueSelected
|
||||
}
|
||||
};
|
||||
return newCategoricalSelection;
|
||||
@@ -70,8 +70,9 @@ const CategoricalSelection = (
|
||||
...state,
|
||||
[action.metadataField]: {
|
||||
...state[action.metadataField],
|
||||
categorySelected: Array.from(
|
||||
state[action.metadataField].categorySelected
|
||||
categorySelected: false,
|
||||
categoryValueSelected: Array.from(
|
||||
state[action.metadataField].categoryValueSelected
|
||||
).fill(false)
|
||||
}
|
||||
};
|
||||
@@ -86,8 +87,9 @@ const CategoricalSelection = (
|
||||
...state,
|
||||
[action.metadataField]: {
|
||||
...state[action.metadataField],
|
||||
categorySelected: Array.from(
|
||||
state[action.metadataField].categorySelected
|
||||
categorySelected: true,
|
||||
categoryValueSelected: Array.from(
|
||||
state[action.metadataField].categoryValueSelected
|
||||
).fill(true)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,10 +183,12 @@ const CrossfilterReducer = (
|
||||
case "categorical metadata filter select":
|
||||
case "categorical metadata filter deselect": {
|
||||
const { categoricalSelection } = nextSharedState;
|
||||
const { world } = prevSharedState;
|
||||
const cat = categoricalSelection[action.metadataField];
|
||||
const col = world.obsAnnotations.col(action.metadataField);
|
||||
return state.select(obsAnnoDimensionName(action.metadataField), {
|
||||
mode: "exact",
|
||||
values: ControlsHelpers.selectedValuesForCategory(cat)
|
||||
values: ControlsHelpers.selectedValuesForCategory(cat, col)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@ Remember that option values can be ANY js type, except undefined/null.
|
||||
{
|
||||
_category_name_1: {
|
||||
// map of option value to index
|
||||
categoryIndices: Map([
|
||||
categoryValueIndices: Map([
|
||||
catval1: index,
|
||||
...
|
||||
])
|
||||
|
||||
// index->selection true/false state
|
||||
categorySelected: [ true/false, true/false, ... ]
|
||||
categoryValueSelected: [ true/false, true/false, ... ]
|
||||
|
||||
// number of options
|
||||
numCategories: number,
|
||||
numCategoryValues: number,
|
||||
|
||||
// isTruncated - true if the options for selection has
|
||||
// been truncated (ie, was too large to implement)
|
||||
@@ -65,18 +65,21 @@ export function createCategoricalSelection(maxCategoryItems, world) {
|
||||
key !== "name" &&
|
||||
summary.categories.length < maxCategoryItems;
|
||||
if (isSelectableCategory) {
|
||||
const [categoryValues, categoryCounts] = topNCategories(summary);
|
||||
const categoryIndices = new Map(categoryValues.map((v, i) => [v, i]));
|
||||
const numCategories = categoryIndices.size;
|
||||
const categorySelected = new Array(numCategories).fill(true);
|
||||
const [categoryValues, categoryValueCounts] = topNCategories(summary);
|
||||
const categoryValueIndices = new Map(
|
||||
categoryValues.map((v, i) => [v, i])
|
||||
);
|
||||
const numCategoryValues = categoryValueIndices.size;
|
||||
const categoryValueSelected = new Array(numCategoryValues).fill(true);
|
||||
const isTruncated = categoryValues.length < summary.numCategories;
|
||||
res[key] = {
|
||||
categoryValues, // array: of natively typed category values
|
||||
categoryIndices, // map: category value (native type) -> category index
|
||||
categorySelected, // array: t/f selection state
|
||||
numCategories, // number: of categories
|
||||
categoryValueIndices, // map: category value (native type) -> category index
|
||||
categoryValueSelected, // array: t/f selection state
|
||||
numCategoryValues, // number: of values in the category
|
||||
isTruncated, // bool: true if list was truncated
|
||||
categoryCounts // array: cardinality of each category
|
||||
categoryValueCounts, // array: cardinality of each category,
|
||||
categorySelected: true // bool - default state for entire category
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -88,12 +91,26 @@ export function createCategoricalSelection(maxCategoryItems, world) {
|
||||
given a categoricalSelection, return the list of all category values
|
||||
where selection state is true (ie, they are selected).
|
||||
*/
|
||||
export function selectedValuesForCategory(categorySelectionState) {
|
||||
const selectedValues = _([...categorySelectionState.categoryIndices])
|
||||
.filter(tuple => categorySelectionState.categorySelected[tuple[1]])
|
||||
.map(tuple => tuple[0])
|
||||
.value();
|
||||
return selectedValues;
|
||||
export function selectedValuesForCategory(categorySelectionState, dfColumn) {
|
||||
const {
|
||||
categorySelected,
|
||||
categoryValueSelected,
|
||||
categoryValueIndices
|
||||
} = categorySelectionState;
|
||||
let selectedValues;
|
||||
if (categorySelected) {
|
||||
selectedValues = new Set(dfColumn.summarize().categories);
|
||||
} else {
|
||||
selectedValues = new Set();
|
||||
}
|
||||
categoryValueIndices.forEach((catIndex, catValue) => {
|
||||
if (!categoryValueSelected[catIndex]) {
|
||||
selectedValues.delete(catValue);
|
||||
} else {
|
||||
selectedValues.add(catValue);
|
||||
}
|
||||
});
|
||||
return [...selectedValues.values()];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user