1+ export const chartType = {
2+ LINE : 'LINE' ,
3+ BAR : 'BAR' ,
4+ DONUT : 'DONUT' ,
5+ } ;
6+
7+ // TODO: find a way to make this list extensible in config
8+ export const nameType = [ 'NAME' , 'TITLE' , 'DESCRIPTION' , 'LABEL' ] ;
9+ export const dataType = [ 'SERIE' , 'SERIES' , 'DATA' , 'VALUE' , 'VALUES' , 'NUM' ] ;
10+ export const timeType = [ 'TIME' , 'PERIOD' , 'MONTH' , 'YEAR' , 'MONTHS' , 'YEARS' , 'DAY' , 'DAYS' , 'HOUR' , 'HOURS' ]
11+
12+ export function detectChart ( { dataset, barLineSwitch = 6 } ) {
13+ let type = null ;
14+ let usableDataset = null ;
15+
16+ const isJustANumber = typeof dataset === 'number' ;
17+ const isJustAString = typeof dataset === 'string' ;
18+
19+ if ( isJustANumber || isJustAString ) {
20+ console . warn ( `The provided dataset (${ dataset } ) is not sufficient to build a chart` ) ;
21+ }
22+
23+ if ( isSimpleArray ( dataset ) ) {
24+
25+ if ( isSimpleArrayOfNumbers ( dataset ) ) {
26+ dataset . length < barLineSwitch ? type = chartType . BAR : type = chartType . LINE ;
27+ usableDataset = dataset ;
28+ }
29+
30+ if ( isSimpleArrayOfObjects ( dataset ) ) {
31+ if ( ! isArrayOfObjectsOfSameDataType ( dataset ) ) {
32+ console . warn ( 'The objects in the dataset array have a different data structure. Either keys or value types are different.' )
33+ return false
34+ }
35+ const keys = Object . keys ( dataset [ 0 ] ) ;
36+ const values = Object . values ( dataset [ 0 ] ) ;
37+ if ( ! keys . some ( key => hasValidDataTypeKey ( key ) ) ) {
38+ console . warn ( 'The data type of the dataset objects in the array must contain one of the following keys: DATA, SERIES, VALUE, VALUES, NUM. Casing is not important.' )
39+ return false ;
40+ }
41+
42+ if ( passesDatatypeCheck ( values , ( v ) => {
43+ return typeof v === 'number'
44+ } ) ) {
45+ type = chartType . DONUT ;
46+ usableDataset = dataset ;
47+ }
48+
49+ if ( passesDatatypeCheck ( values , ( v ) => {
50+ return Array . isArray ( v ) && isSimpleArrayOfNumbers ( v )
51+ } ) ) {
52+ if ( maxLengthOfArrayTypesInArrayOfObjects ( dataset ) > barLineSwitch ) {
53+ type = chartType . LINE
54+ } else {
55+ type = chartType . BAR
56+ }
57+ usableDataset = dataset . map ( d => {
58+ return {
59+ ...d ,
60+ data : getFirstEntryMatch ( d , ( v ) => isSimpleArrayOfNumbers ( v ) )
61+ }
62+ } )
63+ }
64+ dataset = dataset . map ( d => uppercaseKeys ( d ) )
65+ usableDataset = usableDataset . map ( d => uppercaseKeys ( d ) )
66+ }
67+ }
68+
69+ // IS MATRIX
70+ const isMatrix = ! isSimpleArray ( dataset ) ? false : [ ...new Set ( dataset . flatMap ( d => Array . isArray ( d ) ) ) ] [ 0 ] ;
71+
72+ return {
73+ dataset,
74+ type,
75+ usableDataset
76+ }
77+ }
78+
79+
80+ // UTILS
81+
82+ export function isEmptyDataset ( d ) {
83+ return ! d || ( isSimpleArray ( d ) && ! d . length ) ;
84+ }
85+
86+ export function isSimpleArray ( d ) {
87+ return Array . isArray ( d ) ;
88+ }
89+
90+ export function isEmptyObject ( d ) {
91+ return ! isSimpleArray ( d ) && typeof d === 'object' && Object . keys ( d ) . length > 0 ;
92+ }
93+
94+ export function isSimpleArrayOfNumbers ( d ) {
95+ if ( ! isSimpleArray ( d ) || isEmptyDataset ( d ) ) return false ;
96+ const converted = d . map ( v => Number ( v ) ) ;
97+ return ! [ ...new Set ( converted . flatMap ( d => typeof d === 'number' && ! isNaN ( d ) ) ) ] . includes ( false ) ;
98+ }
99+
100+ export function isSimpleArrayOfStrings ( d ) {
101+ if ( ! isSimpleArray ( d ) || isEmptyDataset ( d ) ) return false ;
102+ return ! [ ...new Set ( d . flatMap ( d => typeof d === 'string' ) ) ] . includes ( false ) ;
103+ }
104+
105+ export function isSimpleArrayOfObjects ( d ) {
106+ if ( ! isSimpleArray ( d ) || isEmptyDataset ( d ) ) return false ;
107+ const isArrayOfObjects = ! [ ...new Set ( d . flatMap ( v => typeof v === 'object' && ! Array . isArray ( v ) ) ) ] . includes ( false ) ;
108+ if ( ! isArrayOfObjects ) return false ;
109+ return ! d . map ( v => Object . keys ( v ) . length > 0 ) . includes ( false )
110+ }
111+
112+ export function haveSameStructure ( obj1 , obj2 ) {
113+ const keys1 = Object . keys ( obj1 ) . sort ( ) ;
114+ const keys2 = Object . keys ( obj2 ) . sort ( ) ;
115+ if ( keys1 . length !== keys2 . length ) {
116+ return false ;
117+ }
118+ for ( let i = 0 ; i < keys1 . length ; i += 1 ) {
119+ const key1 = keys1 [ i ] ;
120+ const key2 = keys2 [ i ] ;
121+
122+ if ( key1 !== key2 || typeof obj1 [ key1 ] !== typeof obj2 [ key2 ] ) {
123+ return false ;
124+ }
125+ }
126+ return true ;
127+ }
128+
129+ export function isArrayOfObjectsOfSameDataType ( d ) {
130+ if ( d . length <= 1 ) return true ;
131+ for ( let i = 0 ; i < d . length ; i += 1 ) {
132+ for ( let j = i + 1 ; j < d . length ; j += 1 ) {
133+ if ( ! haveSameStructure ( d [ i ] , d [ j ] ) ) {
134+ return false ;
135+ }
136+ }
137+ }
138+ return true ;
139+ }
140+
141+ export function hasValidDataTypeKey ( key ) {
142+ return dataType . includes ( key . toUpperCase ( ) )
143+ }
144+
145+ export function passesDatatypeCheck ( datapoints , checkTypeFunction ) {
146+ let arr = [ ] ;
147+
148+ for ( let i = 0 ; i < datapoints . length ; i += 1 ) {
149+ arr . push ( checkTypeFunction ( datapoints [ i ] ) )
150+ }
151+ return arr . includes ( true ) ;
152+ }
153+
154+ export function maxLengthOfArrayTypesInArrayOfObjects ( ds ) {
155+ return Math . max ( ...[ ...ds ] . flatMap ( d => {
156+ return Object . values ( d ) . filter ( d => isSimpleArrayOfNumbers ( d ) ) . map ( d => d . length )
157+ } ) )
158+ }
159+
160+ export function getFirstEntryMatch ( datapoint , matchFunction ) {
161+ return Object . values ( datapoint ) . filter ( d => matchFunction ( d ) ) [ 0 ]
162+ }
163+
164+ export function uppercaseKeys ( obj ) {
165+ const newObj = { } ;
166+ for ( let key in obj ) {
167+ if ( obj . hasOwnProperty ( key ) ) {
168+ newObj [ key . toUpperCase ( ) ] = obj [ key ] ;
169+ }
170+ }
171+ return newObj ;
172+ }
173+
174+ const chartDetector = {
175+ dataType,
176+ detectChart,
177+ getFirstEntryMatch,
178+ hasValidDataTypeKey,
179+ isArrayOfObjectsOfSameDataType,
180+ isEmptyDataset,
181+ isEmptyObject,
182+ isSimpleArray,
183+ isSimpleArrayOfNumbers,
184+ isSimpleArrayOfObjects,
185+ isSimpleArrayOfStrings,
186+ maxLengthOfArrayTypesInArrayOfObjects,
187+ nameType,
188+ passesDatatypeCheck,
189+ timeType,
190+ uppercaseKeys,
191+ }
192+
193+ export default chartDetector ;
0 commit comments