1378. Replace Employee ID With The Unique Identifier
On LeetCode ->Problem¶
Given employees and an optional mapping from employee id to unique_id, return every employee's name with their unique_id, or NULL if missing.
Input:
# Employees
---+---------
id | name
---+---------
1 | 'Alice'
7 | 'Bob'
3 | 'Jonathan'
# EmployeeUNI
---+----------
id | unique_id
---+----------
3 | 1
Output:
----------+-----------
unique_id | name
----------+-----------
NULL | 'Alice'
NULL | 'Bob'
1 | 'Jonathan'
Key trick¶
Use Employees as the left table in a LEFT JOIN, because every employee must appear even if they have no matching unique_id.
Trap¶
- Using
INNER JOINdrops employees without aunique_id. - Joining from
EmployeeUNItoEmployeeskeeps only mapped employees. - In Pandas, missing SQL
NULLbecomesNaN.
Why is it interesting?¶
It checks whether you understand the direction and semantics of a LEFT JOIN, which is one of the most common SQL interview patterns.
SQL solution¶
PostgreSQL is identical, so no separate solution is needed.
Pandas solution¶
import pandas as pd
def replace_employee_id(
employees: pd.DataFrame,
employee_uni: pd.DataFrame,
) -> pd.DataFrame:
# Left merge keeps all employees and fills missing unique_id with NaN.
merged = employees.merge(employee_uni, on="id", how="left")
# Return only the required columns.
return merged[["unique_id", "name"]]
Pytest test¶
import sqlite3
import pandas as pd
import pytest
SQL_QUERY = """
SELECT
u.unique_id,
e.name
FROM Employees AS e
LEFT JOIN EmployeeUNI AS u
ON e.id = u.id;
"""
def replace_employee_id(
employees: pd.DataFrame,
employee_uni: pd.DataFrame,
) -> pd.DataFrame:
merged = employees.merge(employee_uni, on="id", how="left")
return merged[["unique_id", "name"]]
def make_employees(rows):
return pd.DataFrame(rows, columns=["id", "name"]).astype(
{"id": "int64", "name": "object"}
)
def make_employee_uni(rows):
return pd.DataFrame(rows, columns=["id", "unique_id"]).astype(
{"id": "int64", "unique_id": "int64"}
)
def normalize_rows(rows):
normalized = []
for unique_id, name in rows:
if pd.isna(unique_id):
unique_id = None
else:
unique_id = int(unique_id)
normalized.append((unique_id, name))
return sorted(normalized, key=lambda row: (row[1], -1 if row[0] is None else row[0]))
@pytest.mark.parametrize(
"employees_rows, employee_uni_rows, expected_rows",
[
(
[(1, "Alice"), (7, "Bob"), (11, "Meir"), (90, "Winston"), (3, "Jonathan")],
[(3, 1), (11, 2), (90, 3)],
[(None, "Alice"), (None, "Bob"), (2, "Meir"), (3, "Winston"), (1, "Jonathan")],
),
(
[(1, "Alice"), (2, "Bob")],
[],
[(None, "Alice"), (None, "Bob")],
),
(
[(1, "Alice"), (2, "Bob")],
[(1, 10), (2, 20)],
[(10, "Alice"), (20, "Bob")],
),
(
[],
[(1, 10)],
[],
),
],
)
def test_replace_employee_id_sql_and_pandas(
employees_rows,
employee_uni_rows,
expected_rows,
):
employees = make_employees(employees_rows)
employee_uni = make_employee_uni(employee_uni_rows)
pandas_result = replace_employee_id(employees, employee_uni)
pandas_rows = list(pandas_result.itertuples(index=False, name=None))
assert normalize_rows(pandas_rows) == normalize_rows(expected_rows)
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE Employees (id INTEGER, name TEXT)")
con.execute("CREATE TABLE EmployeeUNI (id INTEGER, unique_id INTEGER)")
con.executemany(
"INSERT INTO Employees (id, name) VALUES (?, ?)",
employees_rows,
)
con.executemany(
"INSERT INTO EmployeeUNI (id, unique_id) VALUES (?, ?)",
employee_uni_rows,
)
sql_rows = con.execute(SQL_QUERY).fetchall()
assert normalize_rows(sql_rows) == normalize_rows(expected_rows)
con.close()
Comment on my solution¶
Your SQL and Pandas solution are correct.
import pandas as pd
def replace_employee_id(employees: pd.DataFrame, employee_uni: pd.DataFrame) -> pd.DataFrame:
tmp = employees.merge(employee_uni, left_on="id", right_on="id", how="left")
return tmp[["unique_id", "name"]]
Minor notes:
on="id"is shorter than using bothleft_on="id"andright_on="id".- Missing
unique_idvalues appear asNaNin Pandas, which corresponds to SQLNULL.