Skip to content

feat: implement SORT dynamic array function (HF-69)#1707

Open
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/HF-69-sort
Open

feat: implement SORT dynamic array function (HF-69)#1707
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/HF-69-sort

Conversation

@marcin-kordas-hoc

@marcin-kordas-hoc marcin-kordas-hoc commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

What & why

Implements SORT (HF-69, child of HF-28 "Modern dynamic array functions", sibling of the shipped SEQUENCE and of VSTACK/HSTACK). Adds SORT(array, [sort_index], [sort_order], [by_col]) as a dynamic array function.

Tests: handsontable/hyperformula-tests#24 (paired).
Sibling PR: #1708 (UNIQUE / HF-68).
ADR: docs/adr/2026-07-13-sort-unique-array-functions.md.

Behavior

  • Returns an array the same shape as the input.
  • sort_index (default 1): 1-based index into the sort dimension.
  • sort_order: 1 ascending (default) or -1 descending.
  • by_col: FALSE (default) reorders rows; TRUE reorders columns.
  • Ordering reuses ArithmeticHelper (mixed types: numbers < text < logical; empties; locale collation via caseSensitive/accentSensitive) and is stable — ties keep input order.

Design

Mirrors the SEQUENCE/FILTER machinery: sizeOfResultArrayMethod + vectorizationForbidden: true, runtime via runFunction returning SimpleRangeValue/CellError, parse-time size method returning a fresh ArraySize (the input's isRef flag is dropped — a ref-flagged size is treated as scalar and would collapse the spill).

Notes — divergences from Excel (surfaced here + inline + in tests)

  • sort_order is strictly {1, -1}; any other value → #VALUE!. Excel documents only {1,-1}; the reported "sort_order=0 does not error" quirk is undocumented and could not be re-verified against live Excel in this environment, so the strict documented contract was chosen (see ADR dec_2, con_1). Flagged for live-Excel/Kuba confirmation.
  • Multi-key array-constant sort_index (e.g. {1,2}) is not supported in v1 (documented in known-limitations.md; ADR dec_6).
  • In-range errors propagate (first error found; ADR dec_7).

Error-type map

sort_order ∉ {1,-1}#VALUE! (BadMode) · sort_index < 1#VALUE! (LessThanOne) · sort_index > dimension → #VALUE! (ValueLarge) · in-range error → propagate · wrong arity → #N/A.

Definition of Done

  • Production code (SortPlugin.ts, registered via plugin/index.ts)
  • i18n — all 17 language packs (authoritative MS Functions Translator names; enUS inherits enGB)
  • Tests (paired tests PR) — across the standard array-function groups, dual-env safe
  • Docs — built-in-functions.md, known-limitations.md
  • JSDoc on all methods
  • CHANGELOG entry
  • ADR with audit-verified citations

Source: https://app.clickup.com/t/86c89q1tt


Note

Medium Risk
New formula surface in the interpreter with array spill sizing and comparison semantics; mistakes could affect many spreadsheets, but the change is isolated to a new plugin following established SEQUENCE/FILTER patterns.

Overview
Adds Excel-style SORT as a dynamic array function: SORT(Array, [SortIndex], [SortOrder], [ByCol]) returns a spilled range with the same shape as the input, sorting rows (default) or columns by a single 1-based key.

Implementation is a new SortPlugin wired like other array functions (sizeOfResultArrayMethod, vectorizationForbidden), with parse-time size matching the input array (fresh ArraySize so spills are not collapsed). Runtime sorting uses ArithmeticHelper lt/gt for stable ascending/descending order; invalid sort_order (only 1 / -1) or sort_index, non-finite numbers, and the first in-range CellError are handled per the ADR.

Also updates CHANGELOG, built-in functions and known limitations (single-key only, strict sort order, HF comparison rules), registers the plugin in plugin/index.ts, adds SORT to all 17 i18n packs, and documents SORT/UNIQUE decisions in a shared ADR (UNIQUE is out of scope for this PR).

Reviewed by Cursor Bugbot for commit 2fe6a40. Bugbot is set up for automated code reviews on this repo. Configure here.

@netlify

netlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploy Preview for hyperformula-dev-docs ready!

Name Link
🔨 Latest commit 2fe6a40
🔍 Latest deploy log https://app.netlify.com/projects/hyperformula-dev-docs/deploys/6a5b9b0eceda660008daf299
😎 Deploy Preview https://deploy-preview-1707--hyperformula-dev-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@qunabu

qunabu commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Comment thread src/interpreter/plugin/SortPlugin.ts
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Performance comparison of head (2fe6a40) vs base (fd04f77)

                                     testName |   base |   head |  change
-------------------------------------------------------------------------
                                      Sheet A | 504.85 | 501.45 |  -0.67%
                                      Sheet B | 168.11 | 162.26 |  -3.48%
                                      Sheet T | 147.97 | 145.45 |  -1.70%
                                Column ranges | 479.24 | 479.69 |  +0.09%
Sheet A:  change value, add/remove row/column |  16.27 |  15.14 |  -6.95%
 Sheet B: change value, add/remove row/column | 142.76 | 127.08 | -10.98%
                   Column ranges - add column | 150.06 | 144.33 |  -3.82%
                Column ranges - without batch | 470.26 | 434.41 |  -7.62%
                        Column ranges - batch | 114.99 | 113.21 |  -1.55%

@marcin-kordas-hoc
marcin-kordas-hoc requested a review from sequba July 16, 2026 11:58
Add SORT(array, [sort_index], [sort_order], [by_col]) as a dynamic array
function, mirroring the SEQUENCE/FILTER machinery. Ordering reuses
ArithmeticHelper (mixed types, empties, collation) and is stable, so ties
keep input order. sort_order is validated strictly to {1,-1}; sort_index is
bounds-checked; errors in the input range propagate. Output shape equals the
input shape.

Includes i18n for all 17 language packs, docs (built-in-functions,
known-limitations), a changelog entry, and the shared ADR.

Source: https://app.clickup.com/t/86c89q1tt
ADR: docs/adr/2026-07-13-sort-unique-array-functions.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2fe6a40. Configure here.

{argumentType: FunctionArgumentType.BOOLEAN, defaultValue: false, emptyAsDefault: true},
],
vectorizationForbidden: true,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing array arithmetic flag

Medium Severity

SORT omits enableArrayArithmeticForArguments: true, unlike sibling array functions FILTER, VSTACK, and HSTACK. With the default useArrayArithmetic: false, array expressions passed as the first argument are coerced as scalars and fail, so computed arrays cannot be sorted without a global config change or an ARRAYFORMULA wrapper.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2fe6a40. Configure here.

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.21429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 96.87%. Comparing base (fd04f77) to head (2fe6a40).

Files with missing lines Patch % Lines
src/interpreter/plugin/SortPlugin.ts 98.18% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1707      +/-   ##
===========================================
- Coverage    97.17%   96.87%   -0.30%     
===========================================
  Files          176      177       +1     
  Lines        15483    15539      +56     
  Branches      3429     3441      +12     
===========================================
+ Hits         15045    15054       +9     
- Misses         438      485      +47     
Files with missing lines Coverage Δ
src/i18n/languages/csCZ.ts 100.00% <ø> (ø)
src/i18n/languages/daDK.ts 100.00% <ø> (ø)
src/i18n/languages/deDE.ts 100.00% <ø> (ø)
src/i18n/languages/enGB.ts 100.00% <ø> (ø)
src/i18n/languages/esES.ts 100.00% <ø> (ø)
src/i18n/languages/fiFI.ts 100.00% <ø> (ø)
src/i18n/languages/frFR.ts 100.00% <ø> (ø)
src/i18n/languages/huHU.ts 100.00% <ø> (ø)
src/i18n/languages/idID.ts 100.00% <ø> (ø)
src/i18n/languages/itIT.ts 100.00% <ø> (ø)
... and 9 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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