← All guides

Which LibreOffice version do you need for VSTACK, HSTACK, TEXTSPLIT, TAKE and DROP?

Excel’s array-shaping functions — VSTACK, HSTACK, TEXTSPLIT, TAKE, DROP and their relatives — are the functions most likely to decide whether a modern workbook survives a move to LibreOffice Calc. They do not degrade gracefully. Either the release you are running has them, in which case our executed runs show them computing the documented answer, or it does not, in which case every one of them returns #NAME?: the same error you would get from a typo.

We ran the same corpus through four LibreOffice builds — 24.2.0.3, 24.8.7.2, 25.2.0.3 and 25.8.7.3 — and the results sort into three clean groups. Thirteen array-shaping and text-split functions arrive in 25.8.7.3 and are #NAME? in everything older. Eight dynamic-array functions arrived a wave earlier, in 24.8.7.2. And nine more are still #NAME? in the newest build we tested.

The surprise

25.2 is not enough. It is tempting to assume that a 2025 release of LibreOffice has the 2022-era Excel array functions, but our 25.2.0.3 run returns #NAME? for VSTACK, HSTACK, TEXTSPLIT, TAKE, DROP, TOCOL, TOROW, WRAPROWS, WRAPCOLS, CHOOSECOLS, CHOOSEROWS, TEXTBEFORE and TEXTAFTER — all thirteen. They only start evaluating in our 25.8.7.3 run. The gap between “current-ish” and “new enough” is one release, and it is the difference between a working workbook and a sheet full of errors.

What this means for a migration

The practical rule from our runs is short. A workbook that uses any of the thirteen functions in the first table below needs LibreOffice 25.8 or newer. On 24.2, 24.8 or 25.2 those formulas do not compute a different answer — they do not compute at all, and the #NAME? flows downstream into every total, chart and lookup that reads the result. A workbook that stops at the second table’s eight functions (XLOOKUP, FILTER, SORT, UNIQUE, SEQUENCE, LET, SORTBY, XMATCH) needs 24.8 or newer. A workbook that uses anything in the third table has no LibreOffice release in our tested range that will run it, and has to be rewritten.

If you are checking a single formula, paste it into the free compatibility checker linked at the foot of this page; if you are checking a whole file, the Migration Audit button below it takes an .xlsx and flags every formula whose function is missing or behaves differently in the target app. For the full per-function support table across all four builds, see LibreOffice Calc function support by version.

Wave one: thirteen functions that land in 25.8.7.3

Inputs, per case: CHOOSECOLS reads A1:C1 = 1, 2, 3; CHOOSEROWS reads A1:A5 = 10, 20, 30, 40, 50; TAKE and DROP read A1:C3 = 1…9 in reading order; HSTACK reads A1:A2 = 1, 2 and B1:B2 = 3, 4; TOCOL, TOROW and VSTACK read A1:B2 = 1, 2 / 3, 4; WRAPCOLS and WRAPROWS read A1:D1 = 1, 2, 3, 4; the text cases take string literals.

FunctionFormulaExcel (documented)Google SheetsLibreOffice 24.2.0.3, 24.8.7.2 & 25.2.0.3 (executed)LibreOffice 25.8.7.3 (executed)
CHOOSECOLS=CHOOSECOLS(A1:C1,3,1)3, 1Not yet executed#NAME?3 — read-back 3, 1 (matches)
CHOOSEROWS=CHOOSEROWS(A1:A5,4,2)40, 20Not yet executed#NAME?40 — read-back 40, 20 (matches)
DROP=DROP(A1:C3,1,1)5, 6 / 8, 9Not yet executed#NAME?5 — read-back 5, 6, 8, 9 (matches)
HSTACK=HSTACK(A1:A2,B1:B2)1, 3 / 2, 4Not yet executed#NAME?1 — read-back 1, 3, 2, 4 (matches)
TAKE=TAKE(A1:C3,2,2)1, 2 / 4, 5Not yet executed#NAME?1 — read-back 1, 2, 4, 5 (matches)
TEXTAFTER=TEXTAFTER("a-b-c","-")b-cNot yet executed#NAME?b-c
TEXTBEFORE=TEXTBEFORE("a-b-c","-")aNot yet executed#NAME?a
TEXTSPLIT=TEXTSPLIT("a,b,c",",")a, b, cNot yet executed#NAME?a — read-back a, b, c (matches)
TOCOL=TOCOL(A1:B2)1, 2, 3, 4Not yet executed#NAME?1 — read-back 1, 2, 3, 4 (matches)
TOROW=TOROW(A1:B2)1, 2, 3, 4Not yet executed#NAME?1 — read-back 1, 2, 3, 4 (matches)
VSTACK=VSTACK(A1:B1,A2:B2)1, 2 / 3, 4Not yet executed#NAME?1 — read-back 1, 2, 3, 4 (matches)
WRAPCOLS=WRAPCOLS(A1:D1,2)1, 3 / 2, 4Not yet executed#NAME?1 — read-back 1, 3, 2, 4 (matches)
WRAPROWS=WRAPROWS(A1:D1,2)1, 2 / 3, 4Not yet executed#NAME?1 — read-back 1, 2, 3, 4 (matches)

The #NAME? column is one column because all three older builds agreed: every one of these cases returned #NAME? in 24.2.0.3, in 24.8.7.2 and in 25.2.0.3. Of the thirteen, our function dataset records CHOOSECOLS, CHOOSEROWS, HSTACK, TOCOL, TOROW, VSTACK, WRAPCOLS and WRAPROWS as documented in Google Sheets’ function list; it does not record DROP, TAKE, TEXTSPLIT, TEXTBEFORE or TEXTAFTER there. Either way we have not run any of them in Sheets, so that column says so rather than guessing.

Two caveats on the 25.8.7.3 column, both covered in detail elsewhere so we will not repeat the tables here. TEXTAFTER and TEXTBEFORE evaluate in 25.8.7.3 but do not honour the if_not_found fallback argument in our runs, and FILTER’s empty-result case reports a different error than Excel documents — both are measured and explained in FILTER no match: Excel #CALC! vs LibreOffice #N/A. For hand-rolled replacements of the two text functions, see TEXTBEFORE/TEXTAFTER vs LEFT, MID and RIGHT.

Wave two: eight functions that landed in 24.8.7.2

These are the previous generation of dynamic-array functions. They are #NAME? in 24.2.0.3 only; from 24.8.7.2 onward our runs show them computing.

FunctionFormulaExcel (documented)Google SheetsLibreOffice 24.2.0.3 (executed)LibreOffice 24.8.7.2, 25.2.0.3 & 25.8.7.3 (executed)
FILTER=FILTER(A1:A5,B1:B5>2)c, d, eNot yet executed#NAME?c — read-back c, d, e (matches)
LET=LET(x,5,x*2)10Not yet executed#NAME?10
SEQUENCE=SEQUENCE(5)1, 2, 3, 4, 5Not yet executed#NAME?1 — read-back 1, 2, 3, 4, 5 (matches)
SORT=SORT(A1:A5)1, 1, 3, 4, 5Not yet executed#NAME?1 — read-back 1, 1, 3, 4, 5 (matches)
SORTBY=SORTBY(A1:A3,B1:B3)y, z, xNot yet executed#NAME?y — read-back y, z, x (matches)
UNIQUE=UNIQUE(A1:A5)1, 2, 3Not yet executed#NAME?1 — read-back 1, 2, 3 (matches)
XLOOKUP=XLOOKUP("b",A1:A3,B1:B3)2Not yet executed#NAME?2
XMATCH=XMATCH("b",A1:A3)2Not yet executed#NAME?2

Inputs: FILTER reads A1:A5 = a…e with B1:B5 = 1…5; SORT and UNIQUE read A1:A5 = 3, 1, 4, 1, 5 and 1, 2, 2, 3, 1 respectively; SORTBY reads A1:A3 = x, y, z with B1:B3 = 3, 1, 2; XLOOKUP and XMATCH read A1:A3 = a, b, c with B1:B3 = 1, 2, 3. Our dataset records FILTER, LET, SEQUENCE, SORT, UNIQUE and XLOOKUP as documented in Google Sheets, and does not record SORTBY or XMATCH there; none has been executed in Sheets by us.

One wrinkle inside this wave is worth stating because it cuts against the tidy story. A separate SORT case, =SORT(A1:A1) over a single blank cell, returned 0 in 24.8.7.2, #VALUE! in 25.2.0.3, and 0 again in 25.8.7.3. So “landed in 24.8” describes the ordinary path; one degenerate input regressed for a release and came back. It is a reminder that “supported” is per-case, not per-function.

Still unsupported in 25.8.7.3

Nine functions returned #NAME? in every build we ran, including the newest. There is no version in our tested range where these start working, so a migration has to replace them rather than wait for an upgrade.

FunctionFormulaExcel (documented)Google SheetsLibreOffice 25.8.7.3 (executed)
ARRAYTOTEXT=ARRAYTOTEXT({1,2,3})1, 2, 3Not yet executed#NAME?
BYCOL=BYCOL(A1:B2,LAMBDA(c,SUM(c)))4, 6Not yet executed#NAME?
BYROW=BYROW(A1:B2,LAMBDA(r,SUM(r)))3, 7Not yet executed#NAME?
GROUPBY=GROUPBY(A1:A4,B1:B4,SUM)Documented; spilled layout varies with optional arguments, so our case is a presence probe with no fixed expected valueNot yet executed#NAME?
MAKEARRAY=MAKEARRAY(2,2,LAMBDA(r,c,r*c))1, 2 / 2, 4Not yet executed#NAME?
MAP=MAP(A1:A3,LAMBDA(x,x*2))2, 4, 6Not yet executed#NAME?
PIVOTBY=PIVOTBY(A1:A4,B1:B4,C1:C4,SUM)Documented; spilled pivot layout varies with optional arguments, so our case is a presence probe with no fixed expected valueNot yet executed#NAME?
REDUCE=REDUCE(0,A1:A3,LAMBDA(a,b,a+b))6Not yet executed#NAME?
SCAN=SCAN(0,A1:A3,LAMBDA(a,b,a+b))1, 3, 6Not yet executed#NAME?

Seven of these nine — ARRAYTOTEXT, BYCOL, BYROW, MAKEARRAY, MAP, REDUCE and SCAN — returned #NAME? in all four builds: 24.2.0.3, 24.8.7.2, 25.2.0.3 and 25.8.7.3. The remaining two are a narrower claim: GROUPBY and PIVOTBY were added to our corpus later and appear only in our 25.2.0.3 and 25.8.7.3 result files, where both returned #NAME?. We make no claim about them in 24.2.0.3 or 24.8.7.2 because we did not run them there.

Six of the nine are the LAMBDA helper family, and they have their own writeup with the rewrite recipes: see LAMBDA, MAP & SCAN: Excel vs LibreOffice, which also covers what happens to an inline =LAMBDA(x,x*2)(5) call in Calc.

How we measured the spilling cases

This matters for reading the tables honestly, because most of these functions return an array rather than a single value. Each case is written into its own sheet with no cached result, and the file is recalculated by LibreOffice from scratch; the formula sits in the top-left cell of a check range recorded with the case. The single value shown in the LibreOffice columns is that anchor cell — which is why =VSTACK(A1:B1,A2:B2) shows as 1 even though its documented result is 1, 2 / 3, 4.

The harness also reads back every cell of the recorded check range and compares that list, flattened in reading order, against the flattened expected array — that is the “read-back” shown after each anchor value, and where it says matches, every cell in the recorded range equalled the corresponding expected value. So for these cases the honest claim is: the function evaluates (no #NAME?), the anchor cell matches, and the cells inside the recorded check range match value-for-value in reading order. What we did not verify is the shape of the spill — flattening makes a 2×2 block and a 1×4 row indistinguishable — nor anything outside the recorded range, so a result that spilled further than expected would not be caught. For the scalar cases (TEXTAFTER, TEXTBEFORE, LET, XLOOKUP, XMATCH) there is no range: the single returned value is what was compared.

How to migrate safely

Pin the version, if you can. The cheapest fix for wave one is not a rewrite but a requirement: install 25.8 or newer everywhere the workbook will be opened. Our runs show all thirteen functions evaluating there, so a workbook that only uses wave one and wave two functions needs no formula changes at all — it needs a supported release. Where you cannot control the target version, treat 24.8 as the floor and rewrite wave one.

Rewrite wave one with classic functions. =VSTACK(A1:B1,A2:B2) is a literal block of cells, so reference the combined range directly or copy the two pieces into one contiguous block. =HSTACK(A1:A2,B1:B2) is the same trick sideways. =TAKE(A1:C3,2,2) and =DROP(A1:C3,1,1) are sub-ranges: use =INDEX($A$1:$C$3,ROW()-ROW($E$1)+1,COLUMN()-COLUMN($E$1)+1) filled across the target block, or simply point at A1:B2 and B2:C3 when the offsets are fixed. =CHOOSECOLS(A1:C1,3,1) and =CHOOSEROWS(A1:A5,4,2) become one =INDEX(range,row,col) per output cell. =TOCOL(A1:B2) and =TOROW(A1:B2), and the WRAPROWS/WRAPCOLS pair, are index arithmetic: =INDEX($A$1:$B$2,INT((ROW()-1)/2)+1,MOD(ROW()-1,2)+1) filled down reproduces the flatten, with the divisor set to the block width. =TEXTSPLIT("a,b,c",",") becomes LEFT/MID/RIGHT around FIND, or Calc’s Text-to-Columns for a one-off.

Do not rely on catching #NAME?. Wrapping a missing function in =IFERROR(VSTACK(...),"") hides the failure instead of fixing it: the workbook opens clean and reports blanks where data should be, which is worse than a visible error. Fix the version or fix the formula.

Check before you commit to a target. The two buttons below are the fast path: the checker answers “does this one formula survive?” and the Migration Audit answers “which formulas in this file do not?” across a whole .xlsx, in the browser.

Honest limits

The Excel column throughout is Microsoft’s documented behaviour as recorded in our test corpus — we did not run Excel, and nothing on this page is an Excel measurement. Google Sheets has not been run through our harness at all; where we say a function is documented in Sheets that is a claim about our dataset’s record of Google’s function list, not about a result we observed. The LibreOffice columns are executed output, and every version claim on this page traces to one of four result files from LibreOffice Calc 24.2.0.3, 24.8.7.2, 25.2.0.3 and 25.8.7.3. Those are the only four builds we tested: a function we describe as arriving in 25.8.7.3 may have arrived in any release after 25.2.0.3, and we cannot narrow it further. See our methodology for how recalculation is proven rather than assumed.

Check before you migrate