Skip to content

Add an ignore-case option to NaturalStringComparer [minor] - #76

Open
matt-edmondson wants to merge 2 commits into
mainfrom
fix/66-ignore-case
Open

matt-edmondson wants to merge 2 commits into
mainfrom
fix/66-ignore-case

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #66

What changed

NaturalStringComparer can now compare text ignoring case. Ordinal stays the default.

  • There is a new NaturalStringComparer(bool ignoreCase) constructor, plus static Ordinal and OrdinalIgnoreCase instances. new NaturalStringComparer() behaves exactly as before.
  • In ignore-case mode, text chunks are compared character by character after char.ToUpperInvariant, which is how StringComparison.OrdinalIgnoreCase folds. The comparison is still in place and allocates nothing.
  • Every place that compares a text character goes through the same Fold, so the order stays a total order. That includes text against a number's first digit, in the main loop and in the "text runs on into the other side's number" case. In practice no character folds across the ASCII digit range, so the fold on the number-vs-text path never changes a result today. It is there so the two paths can't disagree.
  • Strings that are equal ignoring case still tie-break with string.CompareOrdinal, as [patch] Break natural-order ties ordinally so distinct strings never compare equal #65 does for leading zeros. They sit next to each other and never compare equal. A SortedSet built with OrdinalIgnoreCase keeps both file1 and File1.
  • The README has an "Ignoring case" section.

Tests

The new NaturalStringComparerIgnoreCaseTests covers:

  • the repro from the issue, which sorts to build3.log, Build20.log, file1.txt, File2.txt, file10.txt, README.md
  • the default instance, Ordinal, and new NaturalStringComparer() keeping today's order
  • strings that differ only in case: ordinal ties, staying between their neighbours, and SortedSet keeping all three
  • an antisymmetry, transitivity, and "only ordinally equal strings tie" fuzz with the option on. It mixes both cases with the punctuation that folding moves relative to letters (`_ ^ [ ``), several digit scripts, and numbers next to text
  • allocation-free comparisons with the option on

The new tests fail without the fix:

  • With the fix reverted, they don't compile, because the new API is missing.
  • With a stub that keeps the API but ignores the flag, the repro and neighbour tests fail (2 of 35).

With the fix applied, all 35 tests pass. That count includes the existing Compare_AllocatesNothing and transitivity tests.

Notes


Generated by Claude Code

matt-edmondson and others added 2 commits November 2, 2026 03:33
Text chunks were always compared ordinally, so a capitalised file name
dropped into a block ahead of every lowercase one. A new
NaturalStringComparer(bool ignoreCase) constructor, with static Ordinal
and OrdinalIgnoreCase instances, folds text the way OrdinalIgnoreCase
does. Every text comparison goes through the same fold, including text
against a number's first digit, so the order stays transitive. Strings
that differ only in case still tie-break ordinally and never compare
equal. The default is unchanged and both modes allocate nothing.

Fixes #66

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW3WL4nzXCqdxvXj85f
The README documents the ignore-case option. Editing the same CLAUDE.md
line as the non-generic IComparer PR only made the two PRs conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW3WL4nzXCqdxvXj85f
@sonarqubecloud

sonarqubecloud Bot commented Nov 2, 2026

Copy link
Copy Markdown

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.

NaturalStringComparer has no ignore-case option, so File2.txt and README.md sort ahead of build3.log and file1.txt

1 participant