Quick Overview

Process underwriting and fraud-flag events in order while maintaining a global set of suspicious PII values. Fraud flags seed the set, and a matching underwriting event returns suspicious before adding all its own values so suspicion can propagate to later records.

Detect Fraud by Propagating Suspicious PII

Company: Affirm

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

# Detect Fraud by Propagating Suspicious PII Process a stream of `underwriting` and `fraud_flag` events in order. Maintain a set of suspicious PII values drawn from `address`, `phone`, `email`, and `ssn`. - A `fraud_flag` event adds all of its nonempty PII values and produces `""`. - An `underwriting` event is suspicious when at least one of its PII values is already suspicious. It produces `"1"` when suspicious and `"0"` otherwise. - When an underwriting event is suspicious, add *all* of its nonempty PII values to the suspicious set so suspicion can propagate to later events. Return one result string per input event. ### Function Signature ```python def classify_fraud_events(events: list[dict]) -> list[str]: ... ``` ### Example ```text Input: [ {"event_type": "underwriting", "customer_details": {"address": "A", "phone": "P1", "email": "E1", "ssn": "S1"}}, {"event_type": "fraud_flag", "customer_details": {"address": "A", "phone": "P1", "email": "E1", "ssn": "S1"}}, {"event_type": "underwriting", "customer_details": {"address": "A", "phone": "P2", "email": "E2", "ssn": "S1"}}, {"event_type": "underwriting", "customer_details": {"address": "B", "phone": "P2", "email": "E3", "ssn": "S3"}} ] Output: ["0", "", "1", "1"] ``` ### Constraints - `0 <= len(events) <= 200_000` - Each event's `event_type` is exactly `"underwriting"` or `"fraud_flag"`. - `customer_details` may be absent or `null`. - Recognized PII values, when present, are strings. ### Clarifications - Ignore missing, `null`, and empty-string PII values. - Suspicion is global across recognized fields: a value seen as a phone can match the same string in another recognized field. - A clean underwriting event does not add its PII values. - A suspicious underwriting event adds its values only after deciding its own result; this produces the same result but makes the ordering explicit. - Results preserve input-event order, including one empty string for each `fraud_flag` event. - Do not mutate the input. ### Hints - Extract the recognized nonempty values once per event. - Test intersection before updating the set for an underwriting event.

Overview: Process underwriting and fraud-flag events in order while maintaining a global set of suspicious PII values. Fraud flags seed the set, and a matching underwriting event returns suspicious before adding all its own values so suspicion can propagate to later records.

Read the full Affirm Software Engineer interview experience this question came from

Process underwriting and fraud-flag events in order using one global set of suspicious nonempty address, phone, email, and SSN values. Fraud flags seed the set; a matching underwriting emits 1 and propagates all its values, while a clean underwriting emits 0.

Constraints

  • customer_details may be absent or None.
  • Recognized present PII values must be strings; empty strings are ignored.
  • Suspicion matches globally across field names.
  • One output string is returned for every event.
  • Inputs are not mutated.

Examples

Input: ([{'event_type':'underwriting','customer_details':{'address':'A','phone':'P1','email':'E1','ssn':'S1'}},{'event_type':'fraud_flag','customer_details':{'address':'A','phone':'P1','email':'E1','ssn':'S1'}},{'event_type':'underwriting','customer_details':{'address':'A','phone':'P2','email':'E2','ssn':'S1'}},{'event_type':'underwriting','customer_details':{'address':'B','phone':'P2','email':'E3','ssn':'S3'}}],)

Expected Output: ['0', '', '1', '1']

Explanation: The supplied example demonstrates two propagation steps.

Input: ([],)

Expected Output: []

Explanation: No events produce no results.

Hints

  1. Extract recognized nonempty values once per event.
  2. For underwriting, test intersection before adding values.
  3. Only suspicious underwriting events propagate.

Community answers

Answer by Samyukta

static final List FIELDS = List.of("address", "phone", "email", "ssn"); @SuppressWarnings("unchecked") public List solution(List> events) { Set emitedValues = new HashSet<>(); List result = new ArrayList<>(); for(Map event: events){ String type = (String) event.get("event_type"); Object detailsObj = event.get("customer_details"); List values = new ArrayList<>(); if(detailsObj instanceof Map) { Map details = (Map) detailsObj; for (String field : FIELDS) { String val = (String) details.get(field); if (val != null && !val.isEmpty()) { values.add(val); } } } if ("fraud_flag".equals(type)) { emitedValues.addAll(values); result.add(""); } else if ("underwriting".equals(type)) { boolean match = values.stream().anyMatch(emitedValues::contains); if(match){ result.add("1"); emitedValues.addAll(values); }else { result.add("0"); } }else { result.add(""); } } return result; }

Answer by leni

SUSPICIOUS_PII_TYPES = ("address", "phone", "email", "ssn") def classify_fraud_events(events): suspicious_set = set() output = [] for event in events: customer_details = event.get("customer_details", {}) possible_set = set() for pii_type in SUSPICIOUS_PII_TYPES: value = customer_details.get(pii_type) if value: possible_set.add(value) if event.get("event_type") == "fraud_flag": suspicious_set |= possible_set output.append("") else: if bool(suspicious_set & possible_set): suspicious_set |= possible_set output.append("1") else: output.append("0") return output

Answer by nisargshah1496

def classify_fraud_events(events): """Classifies underwriting events as fraudulent ('1') or clean ('0') based on propagating PII from fraud flags and prior flagged underwriting events.""" suspicious_pii = set() result = [] for event in events: event_type = event.get('event_type') details = event.get('customer_details', {}) # Extract non-empty PII values pii_fields = [details.get(k) for k in ('address', 'phone', 'email', 'ssn') if details.get(k)] if event_type == 'fraud_flag': # Seed the global set with suspicious PII for val in pii_fields: suspicious_pii.add(val) result.append('') elif event_type == 'underwriting': # Check if any PII matches the suspicious set is_fraudulent = any(val in suspicious_pii for val in pii_fields) if is_fraudulent: result.append('1') # Propagate all values from this fraudulent underwriting event for val in pii_fields: suspicious_pii.add(val) else: result.append('0') return result

Answer by akshayjagz

Simple Python: PII_FIELDS = ("address", "phone", "email", "ssn") def classify_fraud_events(events: list[dict]) -> list[str]: suspicious: set[str] = set() results: list[str] = [] for event in events: details = event.get("customer_details") or {} values = { v for f in PII_FIELDS if isinstance(v := details.get(f), str) and v } if event.get("event_type") == "fraud_flag": suspicious |= values results.append("") else: is_suspicious = bool(values & suspicious) # test BEFORE updating if is_suspicious: suspicious |= values results.append("1" if is_suspicious else "0") return results

Loading coding console...