Skip to content

Fix standardise_taxon_rank() mangling 'section' - #292

Open
ehwenk wants to merge 1 commit into
masterfrom
fix/standardise-taxon-rank-sectio-collision
Open

Fix standardise_taxon_rank() mangling 'section'#292
ehwenk wants to merge 1 commit into
masterfrom
fix/standardise-taxon-rank-sectio-collision

Conversation

@ehwenk

@ehwenk ehwenk commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

"sectio" is a literal prefix of its own English translation
("section"), so a fixed substring replacement anywhere in the
input also matched (and mangled) already-English values:
"section"/"subsection"/"zoosection"/"zoosubsection" became
"sectionn"/"subsectionn"/etc.

Anchor the replacement to the end of the string instead --
generalises to any "...section" variant without an allowlist.

Found via real AFD data in traitecoevo/taxonAlign#11.

"sectio" is a literal prefix of its own English translation
("section"), so a fixed substring replacement anywhere in the
input also matched (and mangled) already-English values:
"section"/"subsection"/"zoosection"/"zoosubsection" became
"sectionn"/"subsectionn"/etc.

Anchor the replacement to the end of the string instead --
generalises to any "...section" variant without an allowlist.

Found via real AFD data in traitecoevo/taxonAlign#11.

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

ehwenk commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator Author

Root cause

Of the seven Latin→English rank translations in standardise_taxon_rank(), "sectio" → "section" is the only pair where the find-string is a literal prefix of its own replace-string. That's what caused the bug: a fixed-string substring replacement (gsub(fixed = TRUE)) matches "sectio" anywhere in the input, including inside values that are already English -- "section", "subsection", "zoosection", "zoosubsection" all contain "sectio" as a substring, so the replacement fired again and left the original trailing "n": "sectionn", "subsectionn", etc.

None of the other six pairs (regnum/kingdom, classis/class, ordo/order, familia/family, varietas/variety, forma/form) have this property -- checked each; none is a prefix of its own translation.

Fix and rejected alternative

Anchored the "sectio" replacement to the end of the string (gsub("sectio$", "section", x = .)) rather than matching anywhere. A genuine Latin value ("sectio", "subsectio", ...) always ends in "sectio"; an already-English value ends in "ection", not "sectio" -- so this distinguishes the two cases correctly.

Considered an alternative: skip translation for any input already in an allowlist of known-English terms. Rejected because it would need every current and future "...section" compound spelled out to stay correct. Confirmed the anchored fix instead handles an untested compound for free:

standardise_taxon_rank("supersectio")
#> [1] "supersection"

Verification

standardise_taxon_rank(c("section", "sectio", "subsection", "subsectio",
                          "zoosection", "zoosectio", "zoosubsection", "zoosubsectio"))
#> [1] "section"       "section"       "subsection"    "subsection"
#> [5] "zoosection"    "zoosection"    "zoosubsection" "zoosubsection"

Added a regression test to tests/testthat/test-functions-standardise_names.R that deliberately interleaves already-English and still-Latin values in one input vector (rather than testing each kind in isolation), matching how real data actually looks -- a mix, not neatly pre-sorted batches. testthat::test_file() on that file: 7/7 passing, 0 failures.

Found while building an APCalign-equivalence test suite in the sibling taxonAlign package -- see traitecoevo/taxonAlign#11 for the original report (411/314218 rows affected in a real AFD-derived reference table).

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.

1 participant