Katkat

Audit a Vendor CSV for Gaps, Banned Terms and Name Mismatches

StripeSoftware Engineer · Technical Screen

Last updated

Build a configurable validator for vendor onboarding CSV rows that flags missing fields, out-of-range lengths and banned words, then checks whether a vendor's display name and legal name share enough words.

A payments platform onboards new vendors from a CSV export: one header row naming the columns, then one row per vendor. Write a validator that checks every row against a set of rules supplied as configuration, and reports which rules each row fails. Nothing about field names, required fields, length limits, banned words or how "enough" name overlap is defined should be hardcoded — all of it is configuration, and you should state the shape you chose.

vendor_id,vendor_name,legal_name,notes
1,Acme Robotics,Acme Robotics LLC,Primary supplier
2,,Bright Labs LLC,Missing display name
3,  ACME   Robotics ,acme robotics llc,Spacing and case only
4,Zed,Zed Logistics Inc,Display name too short

Function Signature

def validate_vendor_records(csv_text: str, config: dict) -> list[dict]:

Rules

  • The first line is a header; every following line is one vendor record. A field value may itself contain a comma if quoted, so parse with a real CSV reader rather than splitting on commas.
  • The validator only reports problems — it never edits, reorders, drops, or deduplicates records.
  • config supplies: required_fields, length_limits (per-field (min, max) after trimming), forbidden_words (matched whole-word, case-insensitive, on every field), overlap_fields (a pair of field names to compare), ignored_words (stripped out before comparing, e.g. llc, inc), and min_overlap_ratio.
  • A field counts as missing if it is empty or only whitespace after trimming.
  • For the overlap check: lowercase both overlap_fields values, strip punctuation, split into words, drop ignored_words, then compute the Jaccard ratio |intersection| / |union| of the two word sets. If both fields are non-empty and the ratio is below min_overlap_ratio, flag "name_mismatch".
  • Every row appears in the output, in input order, even with an empty issue list.

Part 1 — Per-field checks

For each record, check every required_fields entry for presence and every length_limits entry for range, and scan every field for forbidden_words.

Part 2 — Cross-field overlap

Using overlap_fields and ignored_words, compute the Jaccard ratio described above and flag records whose two name fields don't look related enough.

Example

config = {
    "required_fields": ["vendor_id", "vendor_name", "legal_name"],
    "length_limits": {"vendor_name": (4, 80), "legal_name": (4, 120)},
    "forbidden_words": ["test", "dummy"],
    "overlap_fields": ("vendor_name", "legal_name"),
    "ignored_words": {"inc", "llc", "ltd", "co", "corp", "company", "the"},
    "min_overlap_ratio": 0.5,
}
# Row 2 (missing vendor_name) -> {"row": 2, "issues": ["missing:vendor_name"]}
# Row 4 (vendor_name "Zed", length 3 < min 4) -> {"row": 4, "issues": ["length:vendor_name"]}

Constraints

  • Up to 10^4 rows and 50 columns.
  • Field values are printable text; forbidden words and ignored words are case-insensitive, whole-word matches.

Hints

Hint 1

Use Python's `csv` module (or an equivalent CSV reader) on the raw text instead of hand-splitting on commas — quoted fields can contain commas.

Hint 2

Keep the per-field checks and the overlap check as two clearly separate passes; it makes testing and reasoning about each rule independent.

Hint 3

Normalize for the overlap check (lowercase, strip punctuation, drop ignored words) before splitting into sets, and handle the case where one side becomes an empty set.

Hint 4

Write tests for: a row with all required fields present but one too short, a row with a forbidden word embedded inside a longer unrelated word (should NOT match), and two fields that share zero words after removing ignored words.

Follow-up questions

  • How would you extend this to flag two different rows as likely duplicates of the same vendor?
  • How would you support a forbidden phrase (multiple words), not just a single banned word?
  • If this validator ran on a 10 GB CSV, how would you change the implementation to avoid loading everything into memory?
  • How would you version the rule configuration so past audit results stay reproducible after the rules change?