Skip to content

Fix groupValue bucketer null-handling; add log-scale bucketing + paired helpers - #19

Merged
julien-f merged 4 commits into
mainfrom
feat/log-range-bucketing
Aug 21, 2026
Merged

Fix groupValue bucketer null-handling; add log-scale bucketing + paired helpers#19
julien-f merged 4 commits into
mainfrom
feat/log-range-bucketing

Conversation

@julien-f

Copy link
Copy Markdown
Member

Summary

Fixes #18:

  1. Bug fix: bucketNumericRange/bucketDatePart now return null for a missing (null/undefined) value instead of silently coercing it — previously Number(null) === 0 merged "no value" into the real 0 bucket, and String(null) === "null" surfaced the literal text "null" as a group header (bucketNumericRange also leaked a literal "NaN" for any non-numeric input). formatNumericRange/formatDatePart gained a missingLabel = '(none)' param for that group.
  2. bucketLogRange/formatLogRange: log-scale groupValue/groupFormat for a right-skewed numeric column (review counts, hours played, file sizes). LogRangeOptions { base, divisions, min } generalizes to plain order-of-magnitude (default), log2/octaves, a half-decade "1-3-10" grid, or any other per-base-cycle split. min: 0 opts out of the below-floor collapse bucket for positive values.
  3. numericRangeGroup/datePartGroup/logRangeGroup: bundle a bucketer with its matching formatter into one { groupValue, groupFormat } pair from a single set of arguments, spreadable into a column def — removes the config-divergence risk of passing the same args to both halves separately.

Changes

  • packages/core/src/logic.ts + tests — the actual fix/features
  • All four adapters (react/vue/solid/vanilla) re-export the new APIs
  • CLAUDE.md, docs/grouped-columns.md, docs/solid-package.md, all package READMEs, CHANGELOG
  • All four demos: salary/joined columns use the new paired helpers, a live null-salary example, and logRangeGroup on the huge-dataset demo

Testing

npm run test, npm run type-check, and npm run build all pass across every package.

🤖 Generated with Claude Code

julien-f and others added 4 commits August 21, 2026 15:35
… bucketing

bucketNumericRange/bucketDatePart silently mishandled a missing (null/
undefined) value instead of routing it to its own group:

- bucketNumericRange: Number(null) === 0, so a missing value merged into
  the same bucket as a real, confirmed 0.
- bucketDatePart: String(null) === "null" (not ''), which fails to parse
  and surfaced as the literal group header text "null". bucketNumericRange
  also leaked the literal text "NaN" for any non-numeric input, since NaN
  isn't nullish and stringifies as-is.

Both now return null for a missing/non-numeric value, which groupData's
existing multiValues stringifies to '' — the same key an *unbucketed*
missing scalar already uses, so no new sentinel is introduced.
formatNumericRange/formatDatePart gained a 3rd `missingLabel = '(none)'`
param rendered for that group.

Also adds two related, previously-requested pieces (GitHub issue #18):

- bucketLogRange/formatLogRange: log-scale groupValue/groupFormat for a
  right-skewed numeric column (review counts, hours played, file sizes)
  spanning several orders of magnitude, where a single linear
  bucketNumericRange step is either too coarse for the long tail or too
  fine for the low end. LogRangeOptions { base, divisions, min }
  generalizes to plain order-of-magnitude (default), log2/octaves
  (base: 2), a half-decade "1-3-10" grid (divisions: [1, 3]), or any
  other per-base-cycle split. A value below `min` (default 1, since log
  is undefined at/below 0 regardless) collapses into a distinct "< min"
  bucket, kept separate from the null "missing" key; pass min: 0 to opt
  out of that collapse for positive values.
- numericRangeGroup/datePartGroup/logRangeGroup: each bundles a bucketer
  with its matching formatter into one { groupValue, groupFormat } pair
  from a single set of arguments, spreadable into a column def — removes
  the config-divergence risk of passing the same step/unit/part/options
  to both halves separately.

Fixes #18

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…pers

Re-export bucketLogRange/formatLogRange, numericRangeGroup/datePartGroup/
logRangeGroup, and LogRangeOptions from every adapter package, alongside
the existing bucketNumericRange/formatNumericRange/bucketDatePart/
formatDatePart re-exports.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cover the missing-value fix and bucketLogRange/numericRangeGroup/
datePartGroup/logRangeGroup in CLAUDE.md (new "Ready-made groupValue
bucketers" section), docs/grouped-columns.md, docs/solid-package.md's
index.ts re-export list, every adapter README's grouping section, and
core's README API reference. CHANGELOG entries added under [Unreleased].

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…cketing

All four demos (react/vue/solid/vanilla):

- Salary/Joined columns switch from bucketNumericRange+formatNumericRange/
  bucketDatePart+formatDatePart to the new numericRangeGroup/datePartGroup
  paired helpers.
- Employee.salary becomes number | null — Eva Müller (already modeling
  "no review yet" via score: null) also gets salary: null (payroll not
  finalized yet), demonstrating the missing-value fix live: her row now
  lands in its own "(none)" salary group instead of being miscounted into
  the $0 bucket. Cell formatting/fmtSalary render "—" for a null salary.
- The Huge dataset demo's Amount column becomes groupable via
  logRangeGroup, exercising bucketLogRange/formatLogRange over 100k rows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@julien-f
julien-f merged commit 49a48d6 into main Aug 21, 2026
1 check passed
@julien-f
julien-f deleted the feat/log-range-bucketing branch August 21, 2026 13:47
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.

groupValue bucketing: null/undefined mishandled by bucketNumericRange/bucketDatePart, and no log-scale bucketing helper

1 participant