Skip to content

Commit

Permalink
feat: numeric legend sorts values (PT-187499873) (#1245)
Browse files Browse the repository at this point in the history
* feat: numeric legend sorts values (PT-187499873)

[#187499873](https://www.pivotaltracker.com/story/show/187499873)
  • Loading branch information
emcelroy authored May 8, 2024
1 parent 6cd866f commit a6f9b04
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 15 deletions.
21 changes: 15 additions & 6 deletions v3/src/components/data-display/models/data-configuration-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {missingColor} from "../../../utilities/color-utils"
import {CaseData} from "../d3-types"
import {AttrRole, TipAttrRoles, graphPlaceToAttrRole} from "../data-display-types"
import {GraphPlace} from "../../axis-graph-shared"
import { numericSortComparator } from "../../../utilities/data-utils"

export const AttributeDescription = types
.model('AttributeDescription', {
Expand Down Expand Up @@ -282,12 +283,20 @@ export const DataConfigurationModel = types
const caseDataArray = this.getUnsortedCaseDataArray(caseArrayNumber),
legendAttrID = self.attributeID('legend')
if (legendAttrID) {
const categories = Array.from(self.categoryArrayForAttrRole('legend'))
caseDataArray.sort((cd1: CaseData, cd2: CaseData) => {
const cd1_Value = self.dataset?.getStrValue(cd1.caseID, legendAttrID) ?? '',
cd2_value = self.dataset?.getStrValue(cd2.caseID, legendAttrID) ?? ''
return categories.indexOf(cd1_Value) - categories.indexOf(cd2_value)
})
if (self.attributeType("legend") === "numeric") {
caseDataArray.sort((cd1: CaseData, cd2: CaseData) => {
const cd1Value = self.dataset?.getNumeric(cd1.caseID, legendAttrID) ?? NaN,
cd2Value = self.dataset?.getNumeric(cd2.caseID, legendAttrID) ?? NaN
return numericSortComparator({a: cd1Value, b: cd2Value, order: "desc"})
})
} else {
const categories = Array.from(self.categoryArrayForAttrRole('legend'))
caseDataArray.sort((cd1: CaseData, cd2: CaseData) => {
const cd1Value = self.dataset?.getStrValue(cd1.caseID, legendAttrID) ?? '',
cd2Value = self.dataset?.getStrValue(cd2.caseID, legendAttrID) ?? ''
return categories.indexOf(cd1Value) - categories.indexOf(cd2Value)
})
}
}
return caseDataArray
},
Expand Down
25 changes: 16 additions & 9 deletions v3/src/components/graph/components/dot-chart-bars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { GraphLayout } from "../models/graph-layout"
import { SubPlotCells } from "../models/sub-plot-cells"
import { mstAutorun } from "../../../utilities/mst-autorun"
import { useChartDots } from "../hooks/use-chart-dots"
import { numericSortComparator } from "../../../utilities/data-utils"

interface IRenderBarCoverProps {
barCovers: IBarCover[]
Expand Down Expand Up @@ -50,10 +51,8 @@ const renderBarCovers = (props: IRenderBarCoverProps) => {

const barCoverDimensions = (props: IBarCoverDimensionsProps) => {
const { subPlotCells, cellIndices, maxInCell, minInCell = 0, primCatsCount } = props
const {
numPrimarySplitBands, numSecondarySplitBands, primaryCellWidth, primaryIsBottom, primarySplitCellWidth,
secondaryCellHeight, secondaryNumericScale
} = subPlotCells
const { numPrimarySplitBands, numSecondarySplitBands, primaryCellWidth, primaryIsBottom, primarySplitCellWidth,
secondaryCellHeight, secondaryNumericScale } = subPlotCells
const { p: primeCatIndex, ep: primeSplitCatIndex, es: secSplitCatIndex } = cellIndices
const adjustedPrimeSplitIndex = primaryIsBottom
? primeSplitCatIndex
Expand Down Expand Up @@ -135,20 +134,28 @@ export const DotChartBars = observer(function DotChartBars({ abovePointsGroupRef
// Create a map of cases grouped by legend value so we don't need to filter all cases per value when
// creating the bar covers.
const caseGroups = new Map()
dataset?.cases.forEach(aCase => {
const legendValue = dataset?.getStrValue(aCase.__id__, legendAttrID)
const primaryValue = dataset?.getStrValue(aCase.__id__, dataConfig.attributeID(primaryAttrRole))
dataConfig.caseDataArray.forEach(aCase => {
const legendValue = dataset?.getStrValue(aCase.caseID, legendAttrID)
const primaryValue = dataset?.getStrValue(aCase.caseID, dataConfig.attributeID(primaryAttrRole))
const primarySplitValue =
dataset?.getStrValue(aCase.__id__, dataConfig.attributeID(primarySplitAttrRole))
dataset?.getStrValue(aCase.caseID, dataConfig.attributeID(primarySplitAttrRole))
const secondarySplitValue =
dataset?.getStrValue(aCase.__id__, dataConfig.attributeID(secondarySplitAttrRole))
dataset?.getStrValue(aCase.caseID, dataConfig.attributeID(secondarySplitAttrRole))
const caseGroupKey =
`${legendValue}-${primaryValue}-${primarySplitValue}-${secondarySplitValue}`
if (!caseGroups.has(caseGroupKey)) {
caseGroups.set(caseGroupKey, [])
}
caseGroups.get(caseGroupKey).push(aCase)
})

// If the legend attribute is numeric, sort legendCats in descending order making sure to handle any NaN
// values for cases that don't have a numeric value for the legend attribute.
if (dataConfig.attributeType("legend") === "numeric") {
legendCats.sort((cat1: string, cat2: string) => {
return numericSortComparator({a: Number(cat1), b: Number(cat2), order: "desc"})
})
}

// For each legend value, create a bar cover
legendCats.forEach((legendCat: string) => {
Expand Down
16 changes: 16 additions & 0 deletions v3/src/utilities/data-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,19 @@ export const valueToString = function (iValue: any): string {
}
return ""
}

interface ICompareProps {
a: number
b: number
order: "asc" | "desc"
}

export const numericSortComparator = function ({a, b, order}: ICompareProps): number {
const aIsNaN = isNaN(a)
const bIsNaN = isNaN(b)

if (aIsNaN && bIsNaN) return 0
if (bIsNaN) return order === "asc" ? 1 : -1
if (aIsNaN) return order === "asc" ? -1 : 1
return order === "asc" ? a - b : b - a
}

0 comments on commit a6f9b04

Please sign in to comment.