PracHub
QuestionsPremiumCoachesLearningGuidesInterview Prep

Quick Overview

This question evaluates a candidate's skills in relational data joining, data integration, and numeric reconciliation for mapping userId to customerName across multiple tables.

  • medium
  • Karat
  • Coding & Algorithms
  • Software Engineer

Join tables to map userId to name

Company: Karat

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

## Problem You are given three tables represented as **2D string arrays** (each row is a record, no headers). You need to produce a mapping from **`userId` → `customerName`** by matching and joining information across tables. ### Table 1: `customers` Each row: `[customerName, email, productName, quantity]` - `quantity` is an integer in string form. - Assumption: **each customer buys only one type of product** (i.e., one `productName` per customer). ### Table 2: `orders` Each row: `[userId, email, orderTotal]` - `orderTotal` is a number in string form (integer or decimal). - `email` **may be null** (or an empty string, depending on how the input is represented). ### Table 3: `products` Each row: `[productName, unitPrice]` - `unitPrice` is a number in string form. ## Matching / Join requirement For each `order` record, identify which customer placed it, then output `userId -> customerName`. Use these matching rules: 1. If `orders.email` is present (non-null/non-empty), match the customer by **email**. 2. If `orders.email` is missing, match by using the fact that: - `orderTotal = quantity * unitPrice` for the customer’s purchased product. - Use `customers.productName` joined to `products.productName` to get `unitPrice`. ## Output Return a mapping/dictionary from `userId` to `customerName` for all orders. ## Assumptions / Edge cases - If multiple customers could match an order (e.g., same computed total), specify and implement a deterministic tie-breaker (e.g., pick lexicographically smallest name) or return all candidates—clarify with the interviewer. - Assume inputs are well-formed aside from possible null/empty emails.

Quick Answer: This question evaluates a candidate's skills in relational data joining, data integration, and numeric reconciliation for mapping userId to customerName across multiple tables.

You are given three tables represented as 2D arrays of strings (each inner list is a row, with no headers). Your task is to build a mapping from each order's `userId` to the corresponding `customerName` by joining information across the tables. Tables: 1) `customers`: each row is `[customerName, email, productName, quantity]` - `quantity` is an integer represented as a string. - Each customer buys exactly one product type (one `productName` per customer). 2) `orders`: each row is `[userId, email, orderTotal]` - `orderTotal` is a number represented as a string (may be integer or decimal). - `email` may be missing, represented as `None` or an empty string `""`. 3) `products`: each row is `[productName, unitPrice]` - `unitPrice` is a number represented as a string. Matching rules for each order: 1) If `orders.email` is present (not `None` and not empty) and matches one or more customer emails, match by email. 2) Otherwise, match by total price using: `orderTotal = quantity * unitPrice`, where `unitPrice` is looked up via `customers.productName` joined with `products.productName`. Ambiguity / tie-breaker: - If multiple customers match by email or by computed total, return the lexicographically smallest `customerName` among the matches. Return a dictionary mapping `userId` (string) to `customerName` (string) for all orders.

Constraints

  • 1 <= len(customers), len(orders), len(products) <= 100000
  • quantity is an integer string that fits in 32-bit signed range
  • unitPrice and orderTotal are valid numeric strings (integer or decimal)
  • orders.email may be None or ""
  • If multiple customers match, choose the lexicographically smallest customerName
  • Inputs are well-formed so that each order can be matched by email or by total

Examples

Input: ([['Alice','a@x.com','Book','2'],['Bob','b@x.com','Pen','5']], [['u1','a@x.com','20'],['u2',None,'10']], [['Book','10'],['Pen','2']])

Expected Output: {'u1': 'Alice', 'u2': 'Bob'}

Explanation: u1 matches by email -> Alice. u2 has no email, total 10 matches Bob because 5*2=10.

Input: ([['Amy','amy@mail','Widget','1'],['Zoe','zoe@mail','Widget','1'],['Bob','bob@mail','Gadget','2']], [['1',None,'9.99'],['2','','9.99'],['3',None,'9.990'],['4','bob@mail','10']], [['Widget','9.99'],['Gadget','5']])

Expected Output: {'1': 'Amy', '2': 'Amy', '3': 'Amy', '4': 'Bob'}

Explanation: Orders 1-3 match by total 9.99; both Amy and Zoe match, pick lexicographically smallest -> Amy. Order 4 matches by email -> Bob.

Input: ([['Carl','c@mail','Service','3']], [['X',None,'59.85']], [['Service','19.95']])

Expected Output: {'X': 'Carl'}

Explanation: Email missing, match by total: 3*19.95 = 59.85 -> Carl.

Input: ([['Dana','d@mail','Item','1']], [['u','unknown@mail','7']], [['Item','7']])

Expected Output: {'u': 'Dana'}

Explanation: Email is present but doesn't match any customer, so fallback to total: 1*7=7 -> Dana.

Input: ([['Ann','dup@mail','A','1'],['Ana','dup@mail','B','2']], [['id','dup@mail','3']], [['A','3'],['B','1.5']])

Expected Output: {'id': 'Ana'}

Explanation: Two customers share the same email; by email rule, choose lexicographically smallest name among matches: 'Ana' < 'Ann'.

Solution

def solution(customers, orders, products):
    """Return mapping {userId: customerName} by joining customers/orders/products.

    Tie-breaker: if multiple customers match (by email or by total), choose
    lexicographically smallest customerName.
    """
    from decimal import Decimal

    # productName -> unitPrice
    price_by_product = {}
    for row in products:
        product, unit_price = row
        price_by_product[product] = Decimal(unit_price)

    # email -> lexicographically smallest customerName with that email
    name_by_email = {}

    # total (Decimal) -> lexicographically smallest customerName with that computed total
    name_by_total = {}

    for row in customers:
        customer_name, email, product_name, qty_s = row
        qty = Decimal(qty_s)
        unit_price = price_by_product[product_name]
        total = qty * unit_price

        # email map
        if email is not None and email != "":
            prev = name_by_email.get(email)
            if prev is None or customer_name < prev:
                name_by_email[email] = customer_name

        # total map
        prev_t = name_by_total.get(total)
        if prev_t is None or customer_name < prev_t:
            name_by_total[total] = customer_name

    result = {}
    for row in orders:
        user_id, email, order_total_s = row

        chosen = None
        if email is not None and email != "":
            chosen = name_by_email.get(email)

        if chosen is None:
            order_total = Decimal(order_total_s)
            chosen = name_by_total[order_total]

        result[user_id] = chosen

    return result

Time complexity: O(C + P + O) where C=len(customers), P=len(products), O=len(orders). Space complexity: O(C + P) for lookup maps.

Last updated: Mar 29, 2026

Related Coding Questions

  • Implement obstacle-course run statistics - Karat (medium)
  • Validate password and list rule violations - Karat (medium)

Loading coding console...

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities
  • Student Access

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.