Skip to content

Add TOROW and TOCOL functions (HF-388, HF-387) - #1828

Draft
Tobiadefami wants to merge 38 commits into
developfrom
feature/HF-387
Draft

Tobiadefami wants to merge 38 commits into
developfrom
feature/HF-387

Conversation

@Tobiadefami

Copy link
Copy Markdown
Collaborator

Context

This PR adds the TOROW and TOCOL dynamic-array functions (HF-388, HF-387). TOROW(array, [ignore], [scan_by_column]) returns an array as one row and TOCOL(array, [ignore], [scan_by_column]) as one column, read by row unless scan_by_column is TRUE.

Important

Stacked on #1796 (DROP), which is stacked on #1722 (TAKE). TOROW and TOCOL reuse the count parser and the whole-column size helper that those PRs introduce, so this branch is based on feature/hf-384-drop. Until both merge, this diff also shows their commits. The TOROW and TOCOL change is the last commit, feat: add TOROW and TOCOL functions (HF-388, HF-387), plus a changelog link commit. Once #1722 and #1796 merge, this branch will be rebased onto develop and only those commits will remain.

Behavior, checked against Microsoft Excel Online (en-US):

  • ignore 0 keeps every element, 1 skips blank cells, 2 skips errors (every error type), 3 skips both. It is truncated toward zero; other values return #VALUE!. An empty-text cell is text, so it is kept.
  • ignore accepts numbers, numeric text, TRUE/FALSE and a blank cell; "" and other text return #VALUE!. scan_by_column accepts numbers, booleans and the text "TRUE"/"FALSE"; "", numeric text and other text return #VALUE!.
  • Blank source cells stay blank elements of the result (ISBLANK is TRUE).
  • When every element is skipped, the result is #N/A with the message "Empty array not allowed.". Excel returns #CALC!, which HyperFormula does not have.
  • Errors are checked in this order: the array, ignore, scan_by_column, then the range of ignore. An empty array argument returns #VALUE!.
  • Scalars, inline arrays, ranges and computed arrays are accepted as sources. The result is a value, not a reference, as in Excel.

Implementation

  • TOROW and TOCOL live in ArrayPlugin, after DROP, as one implementation with the result's orientation as a parameter.
  • Spill-size prediction: with ignore 0 the size is exact. With ignore 1-3, or an ignore that is not a literal, the result's length depends on the data, so the source's size is reserved and the cells after the result are blank, as for FILTER and UNIQUE.
  • Whole-column and whole-row sources use the source sheet's effective size, as TAKE and DROP do. A whole column on the formula's own sheet, or one computed by a formula, spills from the first row only; elsewhere it returns #SPILL!. This keeps the reserved space from making its own source taller each time the formula is entered.
  • An array in ignore or scan_by_column uses its first value. Excel returns one result per element.
  • The function metadata catalogue entries, the changelog, a TOROW and TOCOL functions section in known-limitations.md, and rows in list-of-differences.md are included. The Google Sheets column of the new rows is still being filled in.
  • Localized names were read from Excel Online in every locale by switching the session language and reading the formula bar. Indonesian keeps the English names, because Excel Online shows English function names in that locale.

How did you test your changes?

  • Tests PR: handsontable/hyperformula-tests#96. It adds 144 TOROW and 140 TOCOL tests, generated from 135 cases per function measured in Microsoft Excel Online (en-US) on the same data, plus spill-placement tests for whole-column and whole-row sources.
  • Full Jest suite: 506/506 suites, 6,690 tests passed, 3 skipped. The existing TAKE and DROP tests pass unchanged.
  • Breaking the code on purpose made the tests fail, for example: treating empty text as blank under ignore failed 20 tests, and not skipping errors with ignore 3 failed 8.
  • npm run compile and npm run docs:generate-function-docs pass, and lint reports no errors.

Types of changes

  • Breaking change (a fix or a feature because of which an existing functionality doesn't work as expected anymore)
  • New feature or improvement (a non-breaking change that adds functionality)
  • Bug fix (a non-breaking change that fixes an issue)
  • Additional language file, or a change to an existing language file (translations)
  • Change to the documentation

Related issues:

  1. Depends on Add DROP function (HF-384) #1796 (DROP) and Feature/take function #1722 (TAKE).
  2. Tests: handsontable/hyperformula-tests#96

Checklist:

  • I have reviewed the guidelines about Contributing to HyperFormula and I confirm that my code follows the code style of this project.
  • I have signed the Contributor License Agreement.
  • My change is compliant with the OpenDocument standard.
  • My change is compatible with Microsoft Excel, subject to the documented differences.
  • My change is compatible with Google Sheets.
  • I described my changes in the CHANGELOG.md file.
  • My changes require a documentation update.
  • My changes require a migration guide.

🤖 Generated with Claude Code

Tobiadefami and others added 30 commits August 13, 2026 11:28
TAKE shipped with the English name in 10 of 16 packs while 6 carried a
translation, and VSTACK/HSTACK were English in all 16. Microsoft localizes
all three in most locales, so a user could not type the name their Excel
uses.

Names taken from Microsoft's localized "Excel functions (alphabetical)"
page, one locale at a time. Each row there links to the function's own page
using the English slug in the href while the link text is the localized
name, so the lookup is exact:

    <a href="functions/take-function">WYCINEK</a>

Left as English where Microsoft itself does not translate: TAKE, VSTACK and
HSTACK in Indonesian, and VSTACK/HSTACK in Swedish.

Note that a function's own localized page is not a usable source: for
several locales its syntax block still shows the English name even though
the prose and argument names are translated (the French page shows
"=TAKE(tableau, lignes,[colonnes])" while the product uses PRENDRE).

DEV_DOCS records the lookup method and adds the governing policy: ship a
localized name only when it can be confirmed against the product, and keep
the English name otherwise, since an invented name matches nothing, reads
plausibly enough to be typed first, and fails as #NAME?.

No changelog entry: TAKE, VSTACK and HSTACK are all still in [Unreleased],
so no wrong name has been released.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tobiadefami and others added 7 commits September 3, 2026 22:22
Add the DROP dynamic-array function to ArrayPlugin. DROP removes rows and
columns from the start (positive counts) or end (negative counts) of an
array. Counts truncate toward zero, and a zero, omitted, or empty count
drops nothing. Dropping every row or column returns #N/A, because
HyperFormula has no #CALC! error type.

Spill-size prediction uses statically known counts, so dropped rows and
columns are never reserved. A whole-column or whole-row source spills
from its effective size and fits only when the formula starts within the
number of rows or columns dropped, matching Excel.

TAKE and DROP now share the static count parser, source-range lookup,
effective source sizing, and the slice that reads only the kept cells.

Localized names are confirmed in Excel Online for every language pack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A whole-column or whole-row DROP result fits only when the formula
starts within the number of rows or columns dropped. Size prediction
applied that rule only to statically known counts, so a count from a
cell reserved the source's current height wherever the formula was.
On another sheet that spilled where Excel returns #SPILL!; on the
source's own sheet the reservation made the column taller and the
result threw "Resizing to smaller array".

An unresolved count already counts as dropping nothing for the size
bound; it now does for the placement rule too, so the result fits only
in the first row or column. Documented in list-of-differences.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add two general rows that apply to array functions whose size is
predicted before evaluation: an error result fills the whole reserved
area, and a size that depends on cell values reserves the upper bound,
so an occupied cell inside it gives #SPILL! where Excel spills. Add a
DROP row for ISREF, which is FALSE because DROP returns values rather
than a reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add TOROW and TOCOL to ArrayPlugin as one implementation with the result's
orientation as a parameter, reusing DROP's count parser and whole-column
size helpers. The ignore argument skips blank cells, errors or both; with it,
the spill reserves the source's size and leaves the unused cells blank.

Also add the catalogue entries, the localized names, the known limitations,
the list-of-differences rows and the changelog entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cla-external-contractor-signup

Copy link
Copy Markdown

@Tobiadefami thanks for the pull request. No CLA step needed here — our records show you signed the Contributor License Agreement on 2026-07-31. That signature came from our previous signing form and has been carried over, so there is nothing for you to re-sign.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
hyperformula-docs 89ce2a7 Commit Preview URL

Branch Preview URL
Oct 15 2026, 11:12 PM

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Performance comparison of head (dbfb538) vs base (e7e4aae)

                                     testName |    base |    head |  change
---------------------------------------------------------------------------
                                      Sheet A |  447.97 |  503.14 | +12.32%
                                      Sheet B |  146.82 |  166.82 | +13.62%
                                      Sheet T |  125.76 |     151 | +20.07%
                                Column ranges |  450.72 |  528.43 | +17.24%
                                Sorted lookup | 13478.5 | 15281.2 | +13.37%
Sheet A:  change value, add/remove row/column |   17.26 |   16.41 |  -4.92%
 Sheet B: change value, add/remove row/column |  166.18 |  152.62 |  -8.16%
                   Column ranges - add column |  169.06 |  168.43 |  -0.37%
                Column ranges - without batch |  522.84 |  513.22 |  -1.84%
                        Column ranges - batch |  130.23 |  130.68 |  +0.35%

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants