Many people prefer "one-liner" versions of formulas that look like line noise from a modem in days of yore. Those formulas are none the "more efficient", but if you want an unreadable version, you can use this:
=let(d,Sheet1!A1:H,ncr,2,ncg,2,rs,row(d),cs,column(d),nr,max(index((row(d)-rs+1)*iferror(d<>""))),t,offset(d,0,0,nr),h,{offset(t,0,0,1,ncr),"Category",offset(t,0,ncr,1,ncg)},reduce(h,offset(t,1,ncr,nr-1,columns(t)-ncr),lambda(r,c,if((c="")+mod(column(c)-cs-ncr,ncg),r,{r;{offset(t,row(c)-rs,0,1,ncr),offset(t,0,column(c)-cs,1,1),offset(c,0,0,1,ncg)}}))))
More generally, there are many ways to unpivot data where groups of similar columns repeat across rows. The formula below should be easy to apply to many kinds of use cases. It lets you define the location of data, the number of left-hand side columns to put on every result row, and the number of columns per group to unpivot together. The formula figures out the rest. It does not use split()
to avoid side effects such as coercing the text string 1 2 3
to the date 2 January 2023. It also does not use query()
that will return null
when a value does not match the type of the majority of values in its column.
To unpivot the data shown in the question, choose Insert > Sheet and put the formula in cell A1
of the new sheet:
=let(
data, Sheet1!A1:H,
numColsToRepeat, 2,
numColsPerGroup, 2,
rowStart, row(data),
colStart, column(data),
numRows, max(index((row(data) - rowStart + 1) * iferror(data <> ""))),
table, offset(data, 0, 0, numRows),
headers, {
offset(table, 0, 0, 1, numColsToRepeat),
"Category",
offset(table, 0, numColsToRepeat, 1, numColsPerGroup)
},
unpivot, reduce(
headers, offset(table, 1, numColsToRepeat, numRows - 1, columns(table) - numColsToRepeat),
lambda(
result, cell,
if(
(cell = "") + mod(column(cell) - colStart - numColsToRepeat, numColsPerGroup),
result,
{
result;
{
offset(table, row(cell) - rowStart, 0, 1, numColsToRepeat),
offset(table, 0, column(cell) - colStart, 1, 1),
offset(cell, 0, 0, 1, numColsPerGroup)
}
}
)
)
),
unpivot
)
The formula will weed out records where a value is missing in the full name
column. If you want to keep those records, delete the (cell = "") +
bit.
To further process the results, add a query()
wrapper to the unpivot
in the last line.
The results look like this:
Receipt no. |
Amount |
Category |
Child |
Shirt size |
123456 |
$200.00 |
Child 1 |
John Smith |
S |
123456 |
$200.00 |
Child 2 |
Mary |
L |
123456 |
$200.00 |
Child 3 |
Peter |
S |
4545743 |
$20.00 |
Child 1 |
Lilly |
M |
453333 |
$50.00 |
Child 1 |
Tracy |
L |
453333 |
$50.00 |
Child 2 |
Bill |
M |
453333 |
$50.00 |
Child 3 |
Jill |
M |
3212211231 |
$75.00 |
Child 1 |
Michael |
S |
78764343 |
$55.00 |
Child 1 |
Terry |
L |