// 1. Clean each name: trim whitespace, convert to title case ("Alice Chen")
const sanName = (name) => name.split(" ").filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1).toLowerCase()).join(" ");
// 2. Remove duplicates (case-insensitive comparison)
const dedupNames = (names) => {
const seen = new Set();
return names.filter((n) => {
const key = n.toLowerCase();
if (seen.has(key)) return false;
seen.add(key);
return true;
});
};
// 3. Sort alphabetically
const sortName = (names) => [...names].sort((a, b) => a.localeCompare(b));
// +extra
const flagDups = (names) => {
const counts = {};
names.forEach((n) => (counts[n] = (counts[n] || 0) + 1));
return Object.keys(counts).filter((n) => counts[n] > 1);
};
// test
const rawNames = [
" alice Chen", "BOB martinez ", "alice chen",
" Carlos ruiz", "Diana WONG", "bob Martinez",
" eva Kim ", "Carlos Ruiz"
];
console.assert(
JSON.stringify(rawNames.map(sanName)) === JSON.stringify(["Alice Chen", "Bob Martinez", "Alice Chen", "Carlos Ruiz", "Diana Wong", "Bob Martinez", "Eva Kim", "Carlos Ruiz"]),
"sanName failed"
);
const result = sortName(dedupNames(rawNames.map(sanName)));
console.assert(
JSON.stringify(result) === JSON.stringify(["Alice Chen", "Bob Martinez", "Carlos Ruiz", "Diana Wong", "Eva Kim"]),
"dedup/sort failed", result
);
console.assert(
JSON.stringify(flagDups(rawNames.map(sanName))) === JSON.stringify(["Alice Chen", "Bob Martinez", "Carlos Ruiz"]),
"flagDups failed"
);
Ly8gMS4gQ2xlYW4gZWFjaCBuYW1lOiB0cmltIHdoaXRlc3BhY2UsIGNvbnZlcnQgdG8gdGl0bGUgY2FzZSAoIkFsaWNlIENoZW4iKQpjb25zdCBzYW5OYW1lID0gKG5hbWUpID0+IG5hbWUuc3BsaXQoIiAiKS5maWx0ZXIoQm9vbGVhbikubWFwKCh3KSA9PiB3WzBdLnRvVXBwZXJDYXNlKCkgKyB3LnNsaWNlKDEpLnRvTG93ZXJDYXNlKCkpLmpvaW4oIiAiKTsKCi8vIDIuIFJlbW92ZSBkdXBsaWNhdGVzIChjYXNlLWluc2Vuc2l0aXZlIGNvbXBhcmlzb24pCmNvbnN0IGRlZHVwTmFtZXMgPSAobmFtZXMpID0+IHsKICAgIGNvbnN0IHNlZW4gPSBuZXcgU2V0KCk7CiAgICByZXR1cm4gbmFtZXMuZmlsdGVyKChuKSA9PiB7CiAgICAgICAgY29uc3Qga2V5ID0gbi50b0xvd2VyQ2FzZSgpOwogICAgICAgIGlmIChzZWVuLmhhcyhrZXkpKSByZXR1cm4gZmFsc2U7CiAgICAgICAgc2Vlbi5hZGQoa2V5KTsKICAgICAgICByZXR1cm4gdHJ1ZTsKICAgIH0pOwp9OwoKLy8gMy4gU29ydCBhbHBoYWJldGljYWxseQpjb25zdCBzb3J0TmFtZSA9IChuYW1lcykgPT4gWy4uLm5hbWVzXS5zb3J0KChhLCBiKSA9PiBhLmxvY2FsZUNvbXBhcmUoYikpOwoKLy8gK2V4dHJhCmNvbnN0IGZsYWdEdXBzID0gKG5hbWVzKSA9PiB7CiAgICBjb25zdCBjb3VudHMgPSB7fTsKICAgIG5hbWVzLmZvckVhY2goKG4pID0+IChjb3VudHNbbl0gPSAoY291bnRzW25dIHx8IDApICsgMSkpOwogICAgcmV0dXJuIE9iamVjdC5rZXlzKGNvdW50cykuZmlsdGVyKChuKSA9PiBjb3VudHNbbl0gPiAxKTsKfTsKCi8vIHRlc3QKY29uc3QgcmF3TmFtZXMgPSBbCiAgICAiICBhbGljZSBDaGVuIiwgIkJPQiBtYXJ0aW5leiAiLCAiYWxpY2UgY2hlbiIsCiAgICAiIENhcmxvcyBydWl6IiwgIkRpYW5hIFdPTkciLCAiYm9iIE1hcnRpbmV6IiwKICAgICIgIGV2YSBLaW0gIiwgIkNhcmxvcyBSdWl6IgpdOwoKY29uc29sZS5hc3NlcnQoCiAgICBKU09OLnN0cmluZ2lmeShyYXdOYW1lcy5tYXAoc2FuTmFtZSkpID09PSBKU09OLnN0cmluZ2lmeShbIkFsaWNlIENoZW4iLCAiQm9iIE1hcnRpbmV6IiwgIkFsaWNlIENoZW4iLCAiQ2FybG9zIFJ1aXoiLCAiRGlhbmEgV29uZyIsICJCb2IgTWFydGluZXoiLCAiRXZhIEtpbSIsICJDYXJsb3MgUnVpeiJdKSwKICAgICJzYW5OYW1lIGZhaWxlZCIKKTsKCmNvbnN0IHJlc3VsdCA9IHNvcnROYW1lKGRlZHVwTmFtZXMocmF3TmFtZXMubWFwKHNhbk5hbWUpKSk7Cgpjb25zb2xlLmFzc2VydCgKICAgIEpTT04uc3RyaW5naWZ5KHJlc3VsdCkgPT09IEpTT04uc3RyaW5naWZ5KFsiQWxpY2UgQ2hlbiIsICJCb2IgTWFydGluZXoiLCAiQ2FybG9zIFJ1aXoiLCAiRGlhbmEgV29uZyIsICJFdmEgS2ltIl0pLAogICAgImRlZHVwL3NvcnQgZmFpbGVkIiwgcmVzdWx0Cik7Cgpjb25zb2xlLmFzc2VydCgKICAgIEpTT04uc3RyaW5naWZ5KGZsYWdEdXBzKHJhd05hbWVzLm1hcChzYW5OYW1lKSkpID09PSBKU09OLnN0cmluZ2lmeShbIkFsaWNlIENoZW4iLCAiQm9iIE1hcnRpbmV6IiwgIkNhcmxvcyBSdWl6Il0pLAogICAgImZsYWdEdXBzIGZhaWxlZCIKKTsKCg==