Skip to content

feat: implement UNIQUE dynamic array function (HF-68)#1708

Open
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/HF-68-unique
Open

feat: implement UNIQUE dynamic array function (HF-68)#1708
marcin-kordas-hoc wants to merge 1 commit into
developfrom
feature/HF-68-unique

Conversation

@marcin-kordas-hoc

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

Copy link
Copy Markdown
Collaborator

What & why

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

Tests: handsontable/hyperformula-tests#25 (paired).
Sibling PR: #1707 (SORT / HF-69).
ADR: docs/adr/2026-07-13-sort-unique-array-functions.md.

Behavior

  • Returns the distinct rows (or columns when by_col is TRUE) of the input, preserving first-occurrence order.
  • by_col: FALSE (default) compares rows; TRUE compares columns.
  • exactly_once: TRUE returns only rows/columns occurring exactly once; FALSE (default) returns all distinct.
  • Equality reuses ArithmeticHelper.eqcase-insensitive by default (honors caseSensitive), matching Excel's UNIQUE.
  • Result size is data-dependent; mirrors FILTER (predict input size as upper bound, return the smaller actual result).

Design

Mirrors the FILTER machinery for dynamic-size results: sizeOfResultArrayMethod + vectorizationForbidden: true, runtime via runFunction, parse-time size method returning a fresh ArraySize (drops the input's isRef flag). Deduplication is O(n²) in the number of vectors because locale-aware equality is not trivially hashable — noted in code; acceptable for v1.

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

  • Empty result (only via exactly_once when nothing occurs exactly once) → #N/A. Excel returns #CALC!, which HyperFormula has no type for; mirrors FILTER's empty-result mapping (ADR dec_8).
  • Comparison honors HF's collation config rather than a byte-for-byte Excel oracle (no live Excel in this environment; ADR con_1).
  • In-range errors propagate (first error found; ADR dec_7).

Definition of Done

  • Production code (UniquePlugin.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/86c89q1tq

@netlify

netlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploy Preview for hyperformula-dev-docs ready!

Name Link
🔨 Latest commit 994b88e
🔍 Latest deploy log https://app.netlify.com/projects/hyperformula-dev-docs/deploys/6a553302e51a070008d7fcd9
😎 Deploy Preview https://deploy-preview-1708--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

Add UNIQUE(array, [by_col], [exactly_once]) as a dynamic array function,
mirroring the FILTER machinery for data-dependent result size. Deduplication
preserves first-occurrence order and reuses ArithmeticHelper for equality
(case-insensitive by default, honoring the caseSensitive config). Supports
by_col (unique columns) and exactly_once (values occurring exactly once).
Errors in the input range propagate; an empty exactly_once result yields #N/A.

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/86c89q1tq
ADR: docs/adr/2026-07-13-sort-unique-array-functions.md

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

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Performance comparison of head (994b88e) vs base (033f8ac)

                                     testName |   base |   head | change
------------------------------------------------------------------------
                                      Sheet A | 487.48 |  508.3 | +4.27%
                                      Sheet B | 155.29 | 165.53 | +6.59%
                                      Sheet T | 142.19 | 144.75 | +1.80%
                                Column ranges | 466.42 | 470.77 | +0.93%
Sheet A:  change value, add/remove row/column |  15.24 |  16.13 | +5.84%
 Sheet B: change value, add/remove row/column | 129.14 | 137.04 | +6.12%
                   Column ranges - add column | 148.06 | 153.51 | +3.68%
                Column ranges - without batch | 431.28 | 454.03 | +5.27%
                        Column ranges - batch | 112.47 | 115.76 | +2.93%

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.01493% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.16%. Comparing base (033f8ac) to head (994b88e).

Files with missing lines Patch % Lines
src/interpreter/plugin/UniquePlugin.ts 96.96% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1708      +/-   ##
===========================================
+ Coverage    97.15%   97.16%   +0.01%     
===========================================
  Files          176      177       +1     
  Lines        15404    15471      +67     
  Branches      3409     3426      +17     
===========================================
+ Hits         14966    15033      +67     
  Misses         438      438              
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 1 file 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