FAILURE MAP
← Case archive

FA-95856 / Postal address normalization / Open access

Street line standardizer: suffix is peeled before the post-directional · case 01

"OAK ROAD SOUTH" normalizes to "OAK ROAD S" and the suffix stays unabbreviated.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The suffix check runs first and sees the directional as the last token, so the real suffix is never at the end when it is examined.

VERIFIED REPAIR

Peel the post-directional first, then the suffix, then the pre-directional.

Unsuccessful approach: Moving the pre-directional check ahead of the suffix check abbreviates the street name in "WEST ST" to "W ST".

Case contract

Input one street line of synthetic text. Uppercase it, delete periods, turn commas into spaces and split on whitespace. The first token is the house number. From the remaining body, in this order: a trailing directional becomes the post-directional only if at least two body tokens exist; then a trailing suffix from the table is abbreviated only if at least two body tokens remain; then a leading directional becomes the pre-directional only if at least one name token would remain. Return number, pre, name, suffix, post joined by single spaces.

Why this case matters

Street lines are the dominant key in address matching; a single-token street named after a direction or suffix must not be abbreviated away.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    s = x.upper().replace('.', '').replace(',', ' ')
    t = s.split()
    if not t:
        return ''
    num, body = t[0], t[1:]
    SUF = {'STREET': 'ST', 'ST': 'ST', 'AVENUE': 'AVE', 'AVE': 'AVE', 'BOULEVARD': 'BLVD', 'BLVD': 'BLVD',
           'ROAD': 'RD', 'RD': 'RD', 'LANE': 'LN', 'LN': 'LN', 'COURT': 'CT', 'CT': 'CT'}
    DIR = {'NORTH': 'N', 'N': 'N', 'SOUTH': 'S', 'S': 'S', 'EAST': 'E', 'E': 'E', 'WEST': 'W', 'W': 'W'}
    pre = suf = post = ''
    if len(body) >= 2 and body[-1] in SUF:
        suf = SUF[body[-1]]
        body = body[:-1]
    if len(body) > 1 and body[-1] in DIR:
        post = DIR[body[-1]]
        body = body[:-1]
    if body[1:] and body[0] in DIR:
        pre = DIR[body[0]]
        body = body[1:]
    return ' '.join(p for p in [num, pre] + body + [suf, post] if p)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mixed-case suffix is abbreviated', '107 Oak Street', '107 OAK ST'], ['periods removed from abbreviations', '107 N. OAK Ave.', '107 N OAK AVE'], ['street named for a direction keeps its name', '107 North', '107 NORTH'], ['two-token body with post-directional', '107 OAK WEST', '107 OAK W'], ['suffix followed by post-directional', '107 OAK ROAD SOUTH', '107 OAK RD S'], ['direction-named street with suffix', '107 WEST ST', '107 WEST ST'], ['direction-named street with suffix and post-directional', '107 EAST LANE NORTH', '107 EAST LN N'], ['comma-separated control', '107 S OAK BOULEVARD,', '107 S OAK BLVD']], [['mixed-case suffix is abbreviated', '114 Elm Street', '114 ELM ST'], ['periods removed from abbreviations', '114 N. ELM Ave.', '114 N ELM AVE'], ['street named for a direction keeps its name', '114 North', '114 NORTH'], ['two-token body with post-directional', '114 ELM WEST', '114 ELM W'], ['suffix followed by post-directional', '114 ELM ROAD SOUTH', '114 ELM RD S'], ['direction-named street with suffix', '114 WEST ST', '114 WEST ST'], ['direction-named street with suffix and post-directional', '114 EAST LANE NORTH', '114 EAST LN N'], ['comma-separated control', '114 S ELM BOULEVARD,', '114 S ELM BLVD']], [['mixed-case suffix is abbreviated', '121 Maple Street', '121 MAPLE ST'], ['periods removed from abbreviations', '121 N. MAPLE Ave.', '121 N MAPLE AVE'], ['street named for a direction keeps its name', '121 North', '121 NORTH'], ['two-token body with post-directional', '121 MAPLE WEST', '121 MAPLE W'], ['suffix followed by post-directional', '121 MAPLE ROAD SOUTH', '121 MAPLE RD S'], ['direction-named street with suffix', '121 WEST ST', '121 WEST ST'], ['direction-named street with suffix and post-directional', '121 EAST LANE NORTH', '121 EAST LN N'], ['comma-separated control', '121 S MAPLE BOULEVARD,', '121 S MAPLE BLVD']], [['mixed-case suffix is abbreviated', '128 Birch Street', '128 BIRCH ST'], ['periods removed from abbreviations', '128 N. BIRCH Ave.', '128 N BIRCH AVE'], ['street named for a direction keeps its name', '128 North', '128 NORTH'], ['two-token body with post-directional', '128 BIRCH WEST', '128 BIRCH W'], ['suffix followed by post-directional', '128 BIRCH ROAD SOUTH', '128 BIRCH RD S'], ['direction-named street with suffix', '128 WEST ST', '128 WEST ST'], ['direction-named street with suffix and post-directional', '128 EAST LANE NORTH', '128 EAST LN N'], ['comma-separated control', '128 S BIRCH BOULEVARD,', '128 S BIRCH BLVD']], [['mixed-case suffix is abbreviated', '135 Cedar Street', '135 CEDAR ST'], ['periods removed from abbreviations', '135 N. CEDAR Ave.', '135 N CEDAR AVE'], ['street named for a direction keeps its name', '135 North', '135 NORTH'], ['two-token body with post-directional', '135 CEDAR WEST', '135 CEDAR W'], ['suffix followed by post-directional', '135 CEDAR ROAD SOUTH', '135 CEDAR RD S'], ['direction-named street with suffix', '135 WEST ST', '135 WEST ST'], ['direction-named street with suffix and post-directional', '135 EAST LANE NORTH', '135 EAST LN N'], ['comma-separated control', '135 S CEDAR BOULEVARD,', '135 S CEDAR BLVD']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
mixed-case suffix is abbreviated107 OAK ST107 OAK STPassed
periods removed from abbreviations107 N OAK AVE107 N OAK AVEPassed
street named for a direction keeps its name107 NORTH107 NORTHPassed
two-token body with post-directional107 OAK W107 OAK WPassed
suffix followed by post-directional107 OAK ROAD S107 OAK RD SFailed
direction-named street with suffix107 WEST ST107 WEST STPassed
direction-named street with suffix and post-directional107 E LANE N107 EAST LN NFailed
comma-separated control107 S OAK BLVD107 S OAK BLVDPassed

SHA-256 / fe343f99b1fc2b83896fb333923bcd27c2d54913c44554b1d1af9d0dfb4c08d3

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    s = x.upper().replace('.', '').replace(',', ' ')
    t = s.split()
    if not t:
        return ''
    num, body = t[0], t[1:]
    SUF = {'STREET': 'ST', 'ST': 'ST', 'AVENUE': 'AVE', 'AVE': 'AVE', 'BOULEVARD': 'BLVD', 'BLVD': 'BLVD',
           'ROAD': 'RD', 'RD': 'RD', 'LANE': 'LN', 'LN': 'LN', 'COURT': 'CT', 'CT': 'CT'}
    DIR = {'NORTH': 'N', 'N': 'N', 'SOUTH': 'S', 'S': 'S', 'EAST': 'E', 'E': 'E', 'WEST': 'W', 'W': 'W'}
    pre = suf = post = ''
    if len(body) > 1 and body[-1] in DIR:
        post = DIR[body[-1]]
        body = body[:-1]
    if body[1:] and body[0] in DIR:
        pre = DIR[body[0]]
        body = body[1:]
    if len(body) >= 2 and body[-1] in SUF:
        suf = SUF[body[-1]]
        body = body[:-1]
    return ' '.join(p for p in [num, pre] + body + [suf, post] if p)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mixed-case suffix is abbreviated', '107 Oak Street', '107 OAK ST'], ['periods removed from abbreviations', '107 N. OAK Ave.', '107 N OAK AVE'], ['street named for a direction keeps its name', '107 North', '107 NORTH'], ['two-token body with post-directional', '107 OAK WEST', '107 OAK W'], ['suffix followed by post-directional', '107 OAK ROAD SOUTH', '107 OAK RD S'], ['direction-named street with suffix', '107 WEST ST', '107 WEST ST'], ['direction-named street with suffix and post-directional', '107 EAST LANE NORTH', '107 EAST LN N'], ['comma-separated control', '107 S OAK BOULEVARD,', '107 S OAK BLVD']], [['mixed-case suffix is abbreviated', '114 Elm Street', '114 ELM ST'], ['periods removed from abbreviations', '114 N. ELM Ave.', '114 N ELM AVE'], ['street named for a direction keeps its name', '114 North', '114 NORTH'], ['two-token body with post-directional', '114 ELM WEST', '114 ELM W'], ['suffix followed by post-directional', '114 ELM ROAD SOUTH', '114 ELM RD S'], ['direction-named street with suffix', '114 WEST ST', '114 WEST ST'], ['direction-named street with suffix and post-directional', '114 EAST LANE NORTH', '114 EAST LN N'], ['comma-separated control', '114 S ELM BOULEVARD,', '114 S ELM BLVD']], [['mixed-case suffix is abbreviated', '121 Maple Street', '121 MAPLE ST'], ['periods removed from abbreviations', '121 N. MAPLE Ave.', '121 N MAPLE AVE'], ['street named for a direction keeps its name', '121 North', '121 NORTH'], ['two-token body with post-directional', '121 MAPLE WEST', '121 MAPLE W'], ['suffix followed by post-directional', '121 MAPLE ROAD SOUTH', '121 MAPLE RD S'], ['direction-named street with suffix', '121 WEST ST', '121 WEST ST'], ['direction-named street with suffix and post-directional', '121 EAST LANE NORTH', '121 EAST LN N'], ['comma-separated control', '121 S MAPLE BOULEVARD,', '121 S MAPLE BLVD']], [['mixed-case suffix is abbreviated', '128 Birch Street', '128 BIRCH ST'], ['periods removed from abbreviations', '128 N. BIRCH Ave.', '128 N BIRCH AVE'], ['street named for a direction keeps its name', '128 North', '128 NORTH'], ['two-token body with post-directional', '128 BIRCH WEST', '128 BIRCH W'], ['suffix followed by post-directional', '128 BIRCH ROAD SOUTH', '128 BIRCH RD S'], ['direction-named street with suffix', '128 WEST ST', '128 WEST ST'], ['direction-named street with suffix and post-directional', '128 EAST LANE NORTH', '128 EAST LN N'], ['comma-separated control', '128 S BIRCH BOULEVARD,', '128 S BIRCH BLVD']], [['mixed-case suffix is abbreviated', '135 Cedar Street', '135 CEDAR ST'], ['periods removed from abbreviations', '135 N. CEDAR Ave.', '135 N CEDAR AVE'], ['street named for a direction keeps its name', '135 North', '135 NORTH'], ['two-token body with post-directional', '135 CEDAR WEST', '135 CEDAR W'], ['suffix followed by post-directional', '135 CEDAR ROAD SOUTH', '135 CEDAR RD S'], ['direction-named street with suffix', '135 WEST ST', '135 WEST ST'], ['direction-named street with suffix and post-directional', '135 EAST LANE NORTH', '135 EAST LN N'], ['comma-separated control', '135 S CEDAR BOULEVARD,', '135 S CEDAR BLVD']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
mixed-case suffix is abbreviated107 OAK ST107 OAK STPassed
periods removed from abbreviations107 N OAK AVE107 N OAK AVEPassed
street named for a direction keeps its name107 NORTH107 NORTHPassed
two-token body with post-directional107 OAK W107 OAK WPassed
suffix followed by post-directional107 OAK RD S107 OAK RD SPassed
direction-named street with suffix107 W ST107 WEST STFailed
direction-named street with suffix and post-directional107 E LANE N107 EAST LN NFailed
comma-separated control107 S OAK BLVD107 S OAK BLVDPassed

SHA-256 / 17fd13dcd5841d565bbd92b007596d7225045aa2d6c6c7e86952de32a055c708

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    s = x.upper().replace('.', '').replace(',', ' ')
    t = s.split()
    if not t:
        return ''
    num, body = t[0], t[1:]
    SUF = {'STREET': 'ST', 'ST': 'ST', 'AVENUE': 'AVE', 'AVE': 'AVE', 'BOULEVARD': 'BLVD', 'BLVD': 'BLVD',
           'ROAD': 'RD', 'RD': 'RD', 'LANE': 'LN', 'LN': 'LN', 'COURT': 'CT', 'CT': 'CT'}
    DIR = {'NORTH': 'N', 'N': 'N', 'SOUTH': 'S', 'S': 'S', 'EAST': 'E', 'E': 'E', 'WEST': 'W', 'W': 'W'}
    pre = suf = post = ''
    if len(body) > 1 and body[-1] in DIR:
        post = DIR[body[-1]]
        body = body[:-1]
    if len(body) >= 2 and body[-1] in SUF:
        suf = SUF[body[-1]]
        body = body[:-1]
    if body[1:] and body[0] in DIR:
        pre = DIR[body[0]]
        body = body[1:]
    return ' '.join(p for p in [num, pre] + body + [suf, post] if p)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['mixed-case suffix is abbreviated', '107 Oak Street', '107 OAK ST'], ['periods removed from abbreviations', '107 N. OAK Ave.', '107 N OAK AVE'], ['street named for a direction keeps its name', '107 North', '107 NORTH'], ['two-token body with post-directional', '107 OAK WEST', '107 OAK W'], ['suffix followed by post-directional', '107 OAK ROAD SOUTH', '107 OAK RD S'], ['direction-named street with suffix', '107 WEST ST', '107 WEST ST'], ['direction-named street with suffix and post-directional', '107 EAST LANE NORTH', '107 EAST LN N'], ['comma-separated control', '107 S OAK BOULEVARD,', '107 S OAK BLVD']], [['mixed-case suffix is abbreviated', '114 Elm Street', '114 ELM ST'], ['periods removed from abbreviations', '114 N. ELM Ave.', '114 N ELM AVE'], ['street named for a direction keeps its name', '114 North', '114 NORTH'], ['two-token body with post-directional', '114 ELM WEST', '114 ELM W'], ['suffix followed by post-directional', '114 ELM ROAD SOUTH', '114 ELM RD S'], ['direction-named street with suffix', '114 WEST ST', '114 WEST ST'], ['direction-named street with suffix and post-directional', '114 EAST LANE NORTH', '114 EAST LN N'], ['comma-separated control', '114 S ELM BOULEVARD,', '114 S ELM BLVD']], [['mixed-case suffix is abbreviated', '121 Maple Street', '121 MAPLE ST'], ['periods removed from abbreviations', '121 N. MAPLE Ave.', '121 N MAPLE AVE'], ['street named for a direction keeps its name', '121 North', '121 NORTH'], ['two-token body with post-directional', '121 MAPLE WEST', '121 MAPLE W'], ['suffix followed by post-directional', '121 MAPLE ROAD SOUTH', '121 MAPLE RD S'], ['direction-named street with suffix', '121 WEST ST', '121 WEST ST'], ['direction-named street with suffix and post-directional', '121 EAST LANE NORTH', '121 EAST LN N'], ['comma-separated control', '121 S MAPLE BOULEVARD,', '121 S MAPLE BLVD']], [['mixed-case suffix is abbreviated', '128 Birch Street', '128 BIRCH ST'], ['periods removed from abbreviations', '128 N. BIRCH Ave.', '128 N BIRCH AVE'], ['street named for a direction keeps its name', '128 North', '128 NORTH'], ['two-token body with post-directional', '128 BIRCH WEST', '128 BIRCH W'], ['suffix followed by post-directional', '128 BIRCH ROAD SOUTH', '128 BIRCH RD S'], ['direction-named street with suffix', '128 WEST ST', '128 WEST ST'], ['direction-named street with suffix and post-directional', '128 EAST LANE NORTH', '128 EAST LN N'], ['comma-separated control', '128 S BIRCH BOULEVARD,', '128 S BIRCH BLVD']], [['mixed-case suffix is abbreviated', '135 Cedar Street', '135 CEDAR ST'], ['periods removed from abbreviations', '135 N. CEDAR Ave.', '135 N CEDAR AVE'], ['street named for a direction keeps its name', '135 North', '135 NORTH'], ['two-token body with post-directional', '135 CEDAR WEST', '135 CEDAR W'], ['suffix followed by post-directional', '135 CEDAR ROAD SOUTH', '135 CEDAR RD S'], ['direction-named street with suffix', '135 WEST ST', '135 WEST ST'], ['direction-named street with suffix and post-directional', '135 EAST LANE NORTH', '135 EAST LN N'], ['comma-separated control', '135 S CEDAR BOULEVARD,', '135 S CEDAR BLVD']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
mixed-case suffix is abbreviated107 OAK ST107 OAK STPassed
periods removed from abbreviations107 N OAK AVE107 N OAK AVEPassed
street named for a direction keeps its name107 NORTH107 NORTHPassed
two-token body with post-directional107 OAK W107 OAK WPassed
suffix followed by post-directional107 OAK RD S107 OAK RD SPassed
direction-named street with suffix107 WEST ST107 WEST STPassed
direction-named street with suffix and post-directional107 EAST LN N107 EAST LN NPassed
comma-separated control107 S OAK BLVD107 S OAK BLVDPassed

SHA-256 / 5e3db513a029c237e258c864dba1fe1bf8b1c9eda00c9cbac0c32fd6464ae6dc

Verification & scope

A deterministic toy contract over synthetic, obviously fictitious addresses. The rules are stipulated for this model only and make no claim of conformance with any postal authority standard. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:52:17.441136+00:00.

Case digest / 6c05c890916e7b7aee21ee5219081674fe94ccc1f200b01ee3c87e900006db1d