correctly handle selection of trunctated categories (#781)

This commit is contained in:
Bruce Martin
2019-05-23 08:46:22 -07:00
committed by GitHub
parent 49af278de7
commit ef7c26e799
5 changed files with 56 additions and 35 deletions

View File

@@ -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}

View File

@@ -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]

View File

@@ -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)
}
};

View File

@@ -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)
});
}

View File

@@ -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()];
}
/*