import { ISequence , isSeqEmpty , empty } from '../internal' ; import { colorPool } from './internal' ; import type { ICategoricalColumn , ICategoricalDesc , ICategoricalFilter , ICategoricalsColumn , ICategory , ISetCategoricalFilter , } from './ICategoricalColumn' ; import { DEFAULT_COLOR } from './Column' ; import type { ICompareValue , IDataRow } from './interfaces' ; import { FIRST_IS_MISSING } from './missing' ; internal export function toCategory ( cat , value , nextColor = ( ) => DEFAULT_COLOR ) { if ( typeof cat === 'string' ) { return { name : cat , label : cat , color : nextColor ( ) , value } ; } const name = cat . name == null ? String ( cat . value ) : cat . name ; return { name , label : cat . label || name , color : cat . color || nextColor ( ) , value : cat . value != null ? cat . value : value , } ; } internal export function toCompareCategoryValue ( v ) { if ( v == null ) { return NaN ; } return v . value ; } function findMostFrequent ( rows , valueCache ? ) { const hist = new Map < ICategory | null , number > ( ) ; if ( valueCache ) { valueCache . forEach ( ( cat ) => { hist . set ( cat , ( hist . get ( cat ) || 0 ) + 1 ) ; } ) ; } else { rows . forEach ( ( cat ) => { hist . set ( cat , ( hist . get ( cat ) || 0 ) + 1 ) ; } ) ; } if ( hist . size === 0 ) { return { cat : null , count : 0 , } ; } let topCat = null ; let topCount = 0 ; hist . forEach ( ( count , cat ) => { if ( count > topCount ) { topCat = cat ; topCount = count ; } } ) ; return { cat : topCat , count : topCount , } ; } internal export function toMostFrequentCategoricals ( rows , col ) { if ( isSeqEmpty ( rows ) ) { return empty ( col . dataLength ! ) ; } const maps = empty ( col . dataLength ! ) . map ( ( ) => new Map < ICategory | null , number > ( ) ) ; rows . forEach ( ( row ) => { const vs = col . getCategories ( row ) ; if ( ! vs ) { return ; } for ( let i = 0 ; i < maps . length ; ++ i ) { const hist = maps [ i ] ; const cat = vs [ i ] || null ; hist . set ( cat , ( hist . get ( cat ) || 0 ) + 1 ) ; } } ) ; return maps . map ( ( hist ) => { if ( hist . size === 0 ) { return null ; } let topCat = null ; let topCount = 0 ; hist . forEach ( ( count , cat ) => { if ( count > topCount ) { topCat = cat ; topCount = count ; } } ) ; return topCat ; } ) ; } internal export function toGroupCompareCategoryValue ( rows , col , valueCache ? ) { if ( isSeqEmpty ( rows ) ) { return [ NaN , null ] ; } const mostFrequent = findMostFrequent ( rows . map ( ( d ) => col . getCategory ( d ) ) , valueCache ) ; if ( mostFrequent . cat == null ) { return [ NaN , null ] ; } return [ mostFrequent . cat . value , mostFrequent . cat . name . toLowerCase ( ) ] ; } internal function compareCategory ( a , b ) { const aNull = a == null || Number . isNaN ( a . value ) ; const bNull = b == null || Number . isNaN ( b . value ) ; if ( aNull || a == null ) { return bNull ? 0 : FIRST_IS_MISSING ; } if ( bNull || b == null ) { return - FIRST_IS_MISSING ; } if ( a . value === b . value ) { return a . label . toLowerCase ( ) . localeCompare ( b . label . toLowerCase ( ) ) ; } return a . value - b . value ; } export function toCategories ( desc ) { if ( ! desc . categories ) { return [ ] ; } const nextColor = colorPool ( ) ; const l = desc . categories . length - 1 ; const cats = desc . categories . map ( ( cat , i ) => toCategory ( cat , i / l , nextColor ) ) ; return cats . sort ( compareCategory ) ; } internal function isEmptyFilter ( f ) { return f == null || ( ! f . filterMissing && ( f . filter == null || f . filter === '' ) ) ; } internal export function isEqualCategoricalFilter ( a , b ) { if ( a === b ) { return true ; } if ( a == null || b == null ) { return isEmptyFilter ( a ) === isEmptyFilter ( b ) ; } if ( a . filterMissing !== b . filterMissing || typeof a . filter !== typeof b . filter || Array . isArray ( a . filter ) !== Array . isArray ( b . filter ) ) { return false ; } if ( Array . isArray ( a . filter ) ) { return arrayEquals ( a . filter , b . filter as string [ ] ) ; } return String ( a . filter ) === String ( b . filter ) ; } internal export function isEqualSetCategoricalFilter ( a , b ) { if ( ! isEqualCategoricalFilter ( a , b ) ) { return false ; } const am = a && a . mode ? a . mode : 'every' ; const bm = b && b . mode ? b . mode : 'every' ; return am === bm ; } function arrayEquals < T > ( a , b ) { const al = a != null ? a . length : 0 ; const bl = b != null ? b . length : 0 ; if ( al !== bl ) { return false ; } if ( al === 0 ) { return true ; } return a . every ( ( ai , i ) => ai === b [ i ] ) ; } internal export function isCategoryIncluded ( filter , category ) { if ( filter == null ) { return true ; } if ( category == null || Number . isNaN ( category . value ) ) { return ! filter . filterMissing ; } const filterObj = filter . filter ; if ( Array . isArray ( filterObj ) ) { return filterObj . includes ( category . name ) ; } if ( typeof filterObj === 'string' && filterObj . length > 0 ) { return category . name . toLowerCase ( ) . includes ( filterObj . toLowerCase ( ) ) ; } if ( filterObj instanceof RegExp ) { return filterObj . test ( category . name ) ; } return true ; }