Skip to content

Repeated prefix chain silently drops part of the name in join_on_conjunctions #208

Description

@derek73

Description

The prefix-joining loop in join_on_conjunctions (nameparser/parser.py, the # join prefixes to following lastnames section) locates prefix positions with value-based pieces.index(prefix) lookups while mutating pieces in the same loop. When the same prefix word occurs more than once in a chain, .index() re-finds the wrong occurrence and part of the name is silently dropped instead of joined.

Reproduction

>>> from nameparser import HumanName
>>> HumanName("Juan de la de la Vega").last
'de la Vega'                    # missing the first "de la"
>>> HumanName("Charles van der van der Berg").last
'van der Berg'                  # missing the first "van der"

Expected:

>>> HumanName("Juan de la de la Vega").last
'de la de la Vega'
>>> HumanName("Charles van der van der Berg").last
'van der van der Berg'

Root cause

prefixes = list(filter(self.is_prefix, pieces)) snapshots prefix values up front. Each loop iteration then does i = pieces.index(prefix) to relocate that prefix — but pieces has already been mutated (merged) by prior iterations, and .index() has no way to distinguish "the occurrence this iteration is supposed to act on" from an earlier occurrence of the same string. The existing except ValueError: pass fallback (reusing a stale i from the previous iteration) works for the common case of exactly two repeated tokens in a chain, but breaks down once a prefix word repeats across what should be two separate joined groups.

This is the same family of bug as #100 (fixed via a targeted one-line pieces.index(stop_at, i + 1) fix elsewhere in this method) — the underlying issue is that the algorithm identifies positions by value instead of tracking them positionally through the mutation.

Proposed fix

Replace the value-based .index() lookup with a single forward scan that tracks position (i/j) directly instead of re-deriving it from string equality, absorbing contiguous prefix runs before searching for the next prefix/suffix boundary. Verified locally against the full test suite (1248 existing tests, zero regressions) plus the two repro cases above (both now correct). Draft implementation and TDD plan: docs/superpowers/plans/2026-07-04-join-on-conjunctions-restructure.md (local, not yet committed/pushed).

Environment

Found via code review while simplifying join_on_conjunctions in PR #207 — not a regression from that PR; reproduces identically on master before and after it.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions