Forum Discussion
Double Thunking Works Wonders!
We seem to have a good set of alternative strategies for reshaping multidimensional arrays!
I went a bit off track with my latest workbook and, instead of confining myself to the challenge of reshaping multidimensional arrays, I also set out to apply functions such as BYCOL(arr, SUM) to each of the inner 2D arrays. Rather than introducing INDEX, I used TAKE and DROP (along with MAKEARRAY) to return the submatrices. In theory, that would allow me to apply SUMIFS to each submatrix, though I haven't actually done that.
My function INJECTλ both introduces the function with a helper function and (optionally) reshapes the outer array using TOROW or TOCOL. What was a new experiment for me was to use a function
= INJECTλ(nestedArr, depth, width, SUM, BYCOL, [TOCOL])
with the signature: array, scalar, scalar, eta reduced function, helper function, eta reduced function.
Patrick2788 Encouraged by the success of that function, I confess to tampering with the final section of your function which wraps the vector according to the desired shape:
// "Wrap vector according to desired shape."
WRAP, IF(shape, WRAPCOLS, WRAPROWS),
reshaped, WRAP(unpacked, depth * width),
reshaped
If 'shape' is omitted the default is zero so it will test as FALSE. The thing that interested me, is that WRAPROWS and WRAPCOLS (and hence WRAP) are all TYPE 128, (functions). Such a conditional formula was something of a departure from my normal practice ... and it works (I think)!
I've re-visited this task determined to pull this off with a bit more elegance.
BlockMapλ is the result:
// Function: BlockMapλ
// Author: Patrick H.
// Version: 1.0
// Published: 10/3/2025
// Repo: https://github.com/Patrick2788/Excel-Lambda/blob/main/BlockMap%CE%BB.xlsx
// Description: BlockMapλ reshapes a matrix row or column-wise by specified
// block size (depth x width) with optional parameters for
// shaping by column and transposing anchors.
BlockMapλ =
LAMBDA(
matrix, //Input matrix
depth, //Height of each block
width, //Width of each block
[re_shape_by_column?], //Optional: If TRUE, reshapes column-wise; else row-wise
[transpose_anchors?], //Optional: If TRUE, transpose row and col anchors; else normal anchors
//Validate inputs
Validateλ(matrix,depth,width,re_shape_by_column?,
//Proceed
LET(
//----------Re-shape logic---------------------------------
x,ROUNDUP(ROWS(matrix)/depth,0), //Block rows
y,ROUNDUP(COLUMNS(matrix)/width,0), //Block columns
//Anchors
i,SEQUENCE(x,,1,depth)*SEQUENCE(,y,1,0),
j,SEQUENCE(,y,1,width)*SEQUENCE(x,,1,0),
row_anchor,IF(ISOMITTED(transpose_anchors?),i,TRANSPOSE(i)),
col_anchor,IF(ISOMITTED(transpose_anchors?),j,TRANSPOSE(j)),
//Indices
row_indices,TOCOL(row_anchor)+INT(SEQUENCE(,depth*width,0,1/(width))),
col_indices,TOCOL(col_anchor)+TOROW(IFS(SEQUENCE(depth),SEQUENCE(,width,0,1))),
//Output
ReShapedMatrix,INDEX(matrix,row_indices,col_indices),
output_matrix,IF(re_shape_by_column?,TRANSPOSE(ReShapedMatrix),ReShapedMatrix),
output_matrix
)));
//----------Error Handling---------------------------------
//Validate inputs for BlockMapλ.
Validateλ =
LAMBDA(
matrix,
depth,
width,
re_shape_by_column?,
on_valid,
//Halt if not an array.
IF(NOT(TYPE(matrix)=64),"#MATRIX!",
//Halt if re_shape_by_col? is TRUE and result would return #SPILL! error.
IF((re_shape_by_column?)*(ROWS(matrix)*(COLUMNS(matrix))/(width*depth)^2>16384),"#SPILL-RISK!",
//Halt if result would not be a clean re-shaping.
IF((MOD(ROWS(matrix), depth) <> 0) +
(MOD(COLUMNS(matrix),width)<>0),"#BLOCK MISMATCH!",
//Halt if block size exceeds matrix dimensions or is text.
IF(OR(AND(width=1,depth=1),
depth>ROWS(matrix),width>COLUMNS(matrix),
ISTEXT(width),ISTEXT(depth)),"#DIMENSIONS!",
on_valid
)))))