@@ -39,36 +39,48 @@ sqlpage_chart = (() => {
3939
4040 /**
4141 * Aligns series data points by their x-axis categories, ensuring all series have data points
42- * for each unique category. Missing values are filled with zeros. Categories are sorted.
42+ * for each unique category. Missing values are filled with zeros.
43+ * Categories are ordered by their first appearance across all series.
4344 *
4445 * @example
4546 * // Input series:
4647 * const series = [
47- * { name: "A", data: [{x: "Jan ", y: 10}, {x: "Mar ", y: 30}] },
48- * { name: "B", data: [{x: "Jan ", y: 20 }, {x: "Feb ", y: 25 }] }
48+ * { name: "A", data: [{x: "X2 ", y: 10}, {x: "X3 ", y: 30}] },
49+ * { name: "B", data: [{x: "X1 ", y: 25 }, {x: "X2 ", y: 20 }] }
4950 * ];
5051 *
51- * // Output after align_categories:
52+ * // Output after align_categories (orderedCategories will be ["X2", "X3", "X1"]) :
5253 * // [
53- * // { name: "A", data: [{x: "Feb ", y: 0 }, {x: "Jan ", y: 10 }, {x: "Mar ", y: 30 }] },
54- * // { name: "B", data: [{x: "Feb ", y: 25 }, {x: "Jan ", y: 20 }, {x: "Mar ", y: 0 }] }
54+ * // { name: "A", data: [{x: "X2 ", y: 10 }, {x: "X3 ", y: 30 }, {x: "X1 ", y: 0 }] },
55+ * // { name: "B", data: [{x: "X2 ", y: 20 }, {x: "X3 ", y: 0 }, {x: "X1 ", y: 25 }] }
5556 * // ]
5657 *
57- * @param {Series[string][] } series - Array of series objects, each containing name and data points
58+ * @param {( Series[string]) [] } series - Array of series objects, each containing name and data points
5859 * @returns {Series[string][] } Aligned series with consistent categories across all series
5960 */
6061 function align_categories ( series ) {
61- // Collect all unique categories
62- const categories = new Set ( series . flatMap ( ( s ) => s . data . map ( ( p ) => p . x ) ) ) ;
63- const sortedCategories = Array . from ( categories ) . sort ( ) ;
64-
65- // Create a map of category -> value for each series
62+ const categoriesSet = new Set ( ) ;
63+ const pointers = series . map ( ( _ ) => 0 ) ;
64+ while ( true ) {
65+ const series_idxs = series . flatMap ( ( series , i ) =>
66+ pointers [ i ] < series . data . length ? [ i ] : [ ] ,
67+ ) ;
68+ if ( series_idxs . length === 0 ) break ;
69+ const min_ptr = Math . min ( ...series_idxs . map ( ( i ) => pointers [ i ] ) ) ;
70+ const min_series_idx =
71+ series_idxs . find ( ( i ) => pointers [ i ] === min_ptr ) | 0 ;
72+ const min_series = series [ min_series_idx ] ;
73+ const min_point = min_series . data [ min_ptr ] ;
74+ const new_category = min_point . x ;
75+ if ( ! categoriesSet . has ( new_category ) ) categoriesSet . add ( new_category ) ;
76+ pointers [ min_series_idx ] ++ ;
77+ }
78+ // Create a map of category -> value for each series and rebuild
6679 return series . map ( ( s ) => {
6780 const valueMap = new Map ( s . data . map ( ( point ) => [ point . x , point . y ] ) ) ;
68-
6981 return {
7082 name : s . name ,
71- data : sortedCategories . map ( ( category ) => ( {
83+ data : Array . from ( categoriesSet , ( category ) => ( {
7284 x : category ,
7385 y : valueMap . get ( category ) || 0 ,
7486 } ) ) ,
@@ -116,7 +128,7 @@ sqlpage_chart = (() => {
116128 if ( data . type === "pie" ) {
117129 labels = data . points . map ( ( [ name , x , y ] ) => x || name ) ;
118130 series = data . points . map ( ( [ name , x , y ] ) => y ) ;
119- } else if ( categories && data . type === "bar" )
131+ } else if ( categories && data . type === "bar" && series . length > 1 )
120132 series = align_categories ( series ) ;
121133
122134 const chart_type = data . type || "line" ;
0 commit comments