Fix groupValue bucketer null-handling; add log-scale bucketing + paired helpers - #19
Merged
Conversation
… 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #18:
bucketNumericRange/bucketDatePartnow returnnullfor a missing (null/undefined) value instead of silently coercing it — previouslyNumber(null) === 0merged "no value" into the real0bucket, andString(null) === "null"surfaced the literal text"null"as a group header (bucketNumericRangealso leaked a literal"NaN"for any non-numeric input).formatNumericRange/formatDatePartgained amissingLabel = '(none)'param for that group.bucketLogRange/formatLogRange: log-scalegroupValue/groupFormatfor 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: 0opts out of the below-floor collapse bucket for positive values.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/featuresreact/vue/solid/vanilla) re-export the new APIsdocs/grouped-columns.md,docs/solid-package.md, all package READMEs, CHANGELOGlogRangeGroupon the huge-dataset demoTesting
npm run test,npm run type-check, andnpm run buildall pass across every package.🤖 Generated with Claude Code