@@ -17,8 +17,80 @@ import {
1717 transposeMatrix ,
1818} from "./helpers" ;
1919
20+ function stackHorizontally (
21+ ranges : Arg [ ] ,
22+ options ?: { requireSameRowCount ?: boolean }
23+ ) : Matrix < FunctionResultObject > | EvaluationError {
24+ const matrices = ranges . map ( toMatrix ) ;
25+ const nbRowsArr = matrices . map ( ( m ) => m ?. [ 0 ] ?. length ?? 0 ) ;
26+ const nbRows = Math . max ( ...nbRowsArr ) ;
27+
28+ if ( options ?. requireSameRowCount ) {
29+ const firstLength = nbRowsArr [ 0 ] ;
30+ if ( nbRowsArr . some ( ( len ) => len !== firstLength ) ) {
31+ return new EvaluationError (
32+ _t (
33+ "All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s)." ,
34+ nbRowsArr . join ( ", " )
35+ )
36+ ) ;
37+ }
38+ }
39+
40+ const result : Matrix < FunctionResultObject > = [ ] ;
41+ for ( const matrix of matrices ) {
42+ for ( let col = 0 ; col < matrix . length ; col ++ ) {
43+ // Fill with nulls if needed
44+ const array : FunctionResultObject [ ] = Array ( nbRows ) . fill ( { value : null } ) ;
45+ for ( let row = 0 ; row < matrix [ col ] . length ; row ++ ) {
46+ array [ row ] = matrix [ col ] [ row ] ;
47+ }
48+ result . push ( array ) ;
49+ }
50+ }
51+ return result ;
52+ }
53+
54+ function stackVertically (
55+ ranges : Arg [ ] ,
56+ options ?: { requireSameColCount ?: boolean }
57+ ) : Matrix < FunctionResultObject > | EvaluationError {
58+ const matrices = ranges . map ( toMatrix ) ;
59+ const nbColsArr = matrices . map ( ( m ) => m ?. length ?? 0 ) ;
60+ const nbCols = Math . max ( ...nbColsArr ) ;
61+
62+ if ( options ?. requireSameColCount ) {
63+ const firstLength = nbColsArr [ 0 ] ;
64+ if ( nbColsArr . some ( ( len ) => len !== firstLength ) ) {
65+ return new EvaluationError (
66+ _t (
67+ "All ranges in [[FUNCTION_NAME]] must have the same number of columns (got %s)." ,
68+ nbColsArr . join ( ", " )
69+ )
70+ ) ;
71+ }
72+ }
73+
74+ const nbRows = matrices . reduce ( ( acc , m ) => acc + ( m ?. [ 0 ] ?. length ?? 0 ) , 0 ) ;
75+ const result : Matrix < FunctionResultObject > = generateMatrix ( nbCols , nbRows , ( ) => ( {
76+ value : null ,
77+ } ) ) ;
78+
79+ let currentRow = 0 ;
80+ for ( const matrix of matrices ) {
81+ for ( let col = 0 ; col < matrix . length ; col ++ ) {
82+ for ( let row = 0 ; row < matrix [ col ] . length ; row ++ ) {
83+ result [ col ] [ currentRow + row ] = matrix [ col ] [ row ] ;
84+ }
85+ }
86+ currentRow += matrix [ 0 ] ?. length ?? 0 ;
87+ }
88+
89+ return result ;
90+ }
91+
2092// -----------------------------------------------------------------------------
21- // ARRAY_CONSTRAIN
93+ // ARRAY.CONSTRAIN
2294// -----------------------------------------------------------------------------
2395export const ARRAY_CONSTRAIN = {
2496 description : _t ( "Returns a result array constrained to a specific width and height." ) ,
@@ -55,6 +127,36 @@ export const ARRAY_CONSTRAIN = {
55127 isExported : false ,
56128} satisfies AddFunctionDescription ;
57129
130+ // -----------------------------------------------------------------------------
131+ // ARRAY.LITERAL
132+ // -----------------------------------------------------------------------------
133+ export const ARRAY_LITERAL = {
134+ description : _t (
135+ "Appends ranges vertically and in sequence to return a larger array. All ranges must have the same number of columns."
136+ ) ,
137+ args : [ arg ( "range (any, range<any>, repeating)" , _t ( "The range to be appended." ) ) ] ,
138+ compute : function ( ...ranges : Arg [ ] ) {
139+ return stackVertically ( ranges , { requireSameColCount : true } ) ;
140+ } ,
141+ isExported : false ,
142+ hidden : true ,
143+ } satisfies AddFunctionDescription ;
144+
145+ // -----------------------------------------------------------------------------
146+ // ARRAY.ROW
147+ // -----------------------------------------------------------------------------
148+ export const ARRAY_ROW = {
149+ description : _t (
150+ "Appends ranges horizontally and in sequence to return a larger array. All ranges must have the same number of rows."
151+ ) ,
152+ args : [ arg ( "range (any, range<any>, repeating)" , _t ( "The range to be appended." ) ) ] ,
153+ compute : function ( ...ranges : Arg [ ] ) {
154+ return stackHorizontally ( ranges , { requireSameRowCount : true } ) ;
155+ } ,
156+ isExported : false ,
157+ hidden : true ,
158+ } satisfies AddFunctionDescription ;
159+
58160// -----------------------------------------------------------------------------
59161// CHOOSECOLS
60162// -----------------------------------------------------------------------------
@@ -267,23 +369,8 @@ export const FREQUENCY = {
267369export const HSTACK = {
268370 description : _t ( "Appends ranges horizontally and in sequence to return a larger array." ) ,
269371 args : [ arg ( "range (any, range<any>, repeating)" , _t ( "The range to be appended." ) ) ] ,
270- compute : function ( ...ranges : Arg [ ] ) : Matrix < FunctionResultObject > {
271- const nbRows = Math . max ( ...ranges . map ( ( r ) => r ?. [ 0 ] ?. length ?? 0 ) ) ;
272-
273- const result : Matrix < FunctionResultObject > = [ ] ;
274-
275- for ( const range of ranges ) {
276- const _range = toMatrix ( range ) ;
277- for ( let col = 0 ; col < _range . length ; col ++ ) {
278- //TODO: fill with #N/A for unavailable values instead of zeroes
279- const array : FunctionResultObject [ ] = Array ( nbRows ) . fill ( { value : null } ) ;
280- for ( let row = 0 ; row < _range [ col ] . length ; row ++ ) {
281- array [ row ] = _range [ col ] [ row ] ;
282- }
283- result . push ( array ) ;
284- }
285- }
286- return result ;
372+ compute : function ( ...ranges : Arg [ ] ) {
373+ return stackHorizontally ( ranges ) ;
287374 } ,
288375 isExported : true ,
289376} satisfies AddFunctionDescription ;
@@ -652,26 +739,8 @@ export const TRANSPOSE = {
652739export const VSTACK = {
653740 description : _t ( "Appends ranges vertically and in sequence to return a larger array." ) ,
654741 args : [ arg ( "range (any, range<any>, repeating)" , _t ( "The range to be appended." ) ) ] ,
655- compute : function ( ...ranges : Arg [ ] ) : Matrix < FunctionResultObject > {
656- const nbColumns = Math . max ( ...ranges . map ( ( range ) => toMatrix ( range ) . length ) ) ;
657- const nbRows = ranges . reduce ( ( acc , range ) => acc + toMatrix ( range ) [ 0 ] . length , 0 ) ;
658-
659- const result : Matrix < FunctionResultObject > = Array ( nbColumns )
660- . fill ( [ ] )
661- . map ( ( ) => Array ( nbRows ) . fill ( { value : 0 } ) ) ; // TODO fill with #N/A
662-
663- let currentRow = 0 ;
664- for ( const range of ranges ) {
665- const _array = toMatrix ( range ) ;
666- for ( let col = 0 ; col < _array . length ; col ++ ) {
667- for ( let row = 0 ; row < _array [ col ] . length ; row ++ ) {
668- result [ col ] [ currentRow + row ] = _array [ col ] [ row ] ;
669- }
670- }
671- currentRow += _array [ 0 ] . length ;
672- }
673-
674- return result ;
742+ compute : function ( ...ranges : Arg [ ] ) {
743+ return stackVertically ( ranges ) ;
675744 } ,
676745 isExported : true ,
677746} satisfies AddFunctionDescription ;
0 commit comments