import { CategoryKey } from '../../../../common/category' ; import { Relation } from '../../../../common/text_utils' ; import { LegendPath } from '../../../../state/actions/legend' ; import { Datum , ValueAccessor } from '../../../../utils/common' ; public export const AGGREGATE_KEY = 'value' ; public export const STATISTICS_KEY = 'statistics' ; public export const DEPTH_KEY = 'depth' ; public export const CHILDREN_KEY = 'children' ; public export const INPUT_KEY = 'inputIndex' ; public export const PARENT_KEY = 'parent' ; public export const SORT_INDEX_KEY = 'sortIndex' ; public export const PATH_KEY = 'path' ; public export interface Statistics { globalAggregate : number ; } public export interface NodeDescriptor { [ AGGREGATE_KEY ] : number ; [ DEPTH_KEY ] : number ; [ STATISTICS_KEY ] : Statistics ; [ INPUT_KEY ] ? : Array < number > ; } public export type ArrayEntry = [ Key , ArrayNode ] ; public export type HierarchyOfArrays = Array < ArrayEntry > ; public export interface ArrayNode extends NodeDescriptor { [ CHILDREN_KEY ] : HierarchyOfArrays ; [ PARENT_KEY ] : ArrayNode ; [ SORT_INDEX_KEY ] : number ; [ PATH_KEY ] : LegendPath ; } type HierarchyOfMaps = Map < Key , MapNode > ; interface MapNode extends NodeDescriptor { [ CHILDREN_KEY ] ? : HierarchyOfMaps ; [ PARENT_KEY ] ? : ArrayNode ; } public export const NULL_SMALL_MULTIPLES_KEY = '__null_small_multiples_key__' ; public export const HIERARCHY_ROOT_KEY = '__root_key__' ; public export type PrimitiveValue = string | number | null ; public export type Key = CategoryKey ; public export type Sorter = ( a : number , b : number ) => number ; public export type NodeSorter = ( a : ArrayEntry , b : ArrayEntry ) => number ; public export const entryKey = ( [ key ] ) => key ; public export const entryValue = ( [ , value ] ) => value ; public export function depthAccessor ( n ) { return entryValue ( n ) [ DEPTH_KEY ] ; } public export function aggregateAccessor ( n ) { return entryValue ( n ) [ AGGREGATE_KEY ] ; } public export function parentAccessor ( n ) { return entryValue ( n ) [ PARENT_KEY ] ; } public export function childrenAccessor ( n ) { return entryValue ( n ) [ CHILDREN_KEY ] ; } public export function sortIndexAccessor ( n ) { return entryValue ( n ) [ SORT_INDEX_KEY ] ; } public export function pathAccessor ( n ) { return entryValue ( n ) [ PATH_KEY ] ; } public export function getNodeName ( node ) { const index = node [ SORT_INDEX_KEY ] ; const arrayEntry = node [ PARENT_KEY ] [ CHILDREN_KEY ] [ index ] ; return entryKey ( arrayEntry ) ; } internal export function groupByRollup ( keyAccessors , valueAccessor , { reducer , identity , } , factTable ) { const statistics = { globalAggregate : NaN , } ; const reductionMap = factTable . reduce ( ( p , n , index ) => { const keyCount = keyAccessors . length ; let pointer = p ; keyAccessors . forEach ( ( keyAccessor , i ) => { const key = keyAccessor ( n , index ) ; const last = i === keyCount - 1 ; const node = pointer . get ( key ) ; const inputIndices = node ?. [ INPUT_KEY ] ?? [ ] ; const childrenMap = node ?. [ CHILDREN_KEY ] ?? new Map ( ) ; const aggregate = node ?. [ AGGREGATE_KEY ] ?? identity ( ) ; const reductionValue = reducer ( aggregate , valueAccessor ( n ) ) ; pointer . set ( key , { [ AGGREGATE_KEY ] : reductionValue , [ STATISTICS_KEY ] : statistics , [ INPUT_KEY ] : [ ... inputIndices , index ] , [ DEPTH_KEY ] : i , ... ( ! last && { [ CHILDREN_KEY ] : childrenMap } ) , } ) ; if ( childrenMap ) { pointer = childrenMap ; } } ) ; return p ; } , new Map ( ) ) ; if ( reductionMap . get ( HIERARCHY_ROOT_KEY ) !== undefined ) { statistics . globalAggregate = ( reductionMap . get ( HIERARCHY_ROOT_KEY ) as MapNode ) [ AGGREGATE_KEY ] ; } return reductionMap ; } function getRootArrayNode ( ) { const children = [ ] ; const bootstrap = { [ AGGREGATE_KEY ] : NaN , [ DEPTH_KEY ] : NaN , [ CHILDREN_KEY ] : children , [ INPUT_KEY ] : [ ] as number [ ] , [ PATH_KEY ] : [ ] as LegendPath , [ SORT_INDEX_KEY ] : 0 , [ STATISTICS_KEY ] : { globalAggregate : 0 } , } ; ( bootstrap as ArrayNode ) [ PARENT_KEY ] = bootstrap as ArrayNode ; return bootstrap as ArrayNode ; } internal export function mapsToArrays ( root , sortSpecs , innerGroups ) { const groupByMap = ( node , parent ) => { const items = Array . from ( node , ( [ key , value ] ) : ArrayEntry => { const valueElement = value [ CHILDREN_KEY ] ; const resultNode = { [ AGGREGATE_KEY ] : NaN , [ STATISTICS_KEY ] : { globalAggregate : NaN } , [ CHILDREN_KEY ] : [ ] , [ DEPTH_KEY ] : NaN , [ SORT_INDEX_KEY ] : NaN , [ PARENT_KEY ] : parent , [ INPUT_KEY ] : [ ] , [ PATH_KEY ] : [ ] , } ; const newValue = Object . assign ( resultNode , value , valueElement && { [ CHILDREN_KEY ] : groupByMap ( valueElement , resultNode ) } ) ; return [ key , newValue ] ; } ) ; if ( sortSpecs . some ( ( s ) => s !== null ) ) { items . sort ( ( e1 , e2 ) => { const node1 = e1 [ 1 ] ; const node2 = e2 [ 1 ] ; if ( node1 [ DEPTH_KEY ] !== node2 [ DEPTH_KEY ] ) return node1 [ DEPTH_KEY ] - node2 [ DEPTH_KEY ] ; const depth = node1 [ DEPTH_KEY ] ; const sorterWithinLayer = sortSpecs [ depth ] ; return sorterWithinLayer ? sorterWithinLayer ( e1 , e2 ) : node2 . value - node1 . value ; } ) ; } return items . map ( ( n , i ) => { entryValue ( n ) . sortIndex = i ; return n ; } ) ; } ; const tree = groupByMap ( root , getRootArrayNode ( ) ) ; const buildPaths = ( [ key , mapNode ] , currentPath ) => { const newPath = [ ... currentPath , { index : mapNode [ SORT_INDEX_KEY ] , value : key } ] ; mapNode [ PATH_KEY ] = newPath ; mapNode . children . forEach ( ( entry ) => buildPaths ( entry , newPath ) ) ; } ; buildPaths ( tree [ 0 ] , innerGroups ) ; return tree ; } internal export function mapEntryValue ( entry ) { return entryValue ( entry ) [ AGGREGATE_KEY ] ; } internal export const aggregators = { one : { identity : ( ) => 0 , reducer : ( ) => 1 , } , count : { identity : ( ) => 0 , reducer : ( r ) => r + 1 , } , sum : { identity : ( ) => 0 , reducer : ( r , n ) => r + n , } , min : { identity : ( ) => Infinity , reducer : ( r , n ) => Math . min ( r , n ) , } , max : { identity : ( ) => - Infinity , reducer : ( r , n ) => Math . max ( r , n ) , } , min0 : { identity : ( ) => 0 , reducer : ( r , n ) => Math . min ( r , n ) , } , max0 : { identity : ( ) => 0 , reducer : ( r , n ) => Math . max ( r , n ) , } , } ;