/** * TWO ROWS THAT READ THE SAME ARE A CHOICE NOBODY CAN MAKE. * * MEASURED at the front desk, booking for a patient at Great River: the practitioner selector offered * * Zztest Secondclinician - Southeast Iowa Regional Medical Center * Dr. Practitioner One - West Burlington Hospital * Dr. Practitioner One - Southeast Iowa Regional Medical Center * Dr. Practitioner One - West Burlington Hospital <- identical to the second row * * The rows are one per schedule BLOCK and the label is per person-and-place, so a practitioner with two * blocks at one site produces a duplicate. The label was already disambiguated by location, on the * assumption - written into its own comment - that a practitioner has one schedule per location. A * second block at the same site is exactly the case that assumption does not cover. * * ONLY THE ROWS THAT COLLIDE GET THE EXTRA DETAIL, and that is the whole point rather than an * optimisation. Appending a date range to every row buys nothing and costs a scannable list: the reader * has to read a suffix on eight rows to find the one place it matters. A distinguisher that appears only * where there is something to distinguish is also self-explaining - it shows up next to its twin. * * IT NAMES A FACT ABOUT THE THING, NEVER AN INDEX. "Dr. Practitioner One - West Burlington Hospital (2)" * tells a receptionist which row it is and nothing about which diary they are booking into, which is the * question they actually have. A caller that has no distinguishing fact to offer gets the plain label * back and the rows stay identical - that is then a statement about the data rather than about the list, * and it is not this helper's to invent one. */ export interface DistinguishOptions { /** Stable key for the item, so the result can be looked up while rendering. */ key: (item: T) => string; /** What the row says today. */ label: (item: T) => string; /** The fact that separates two otherwise identical rows, or nothing if there is none. */ detail: (item: T) => string | undefined; } /** * A label per item, with the detail appended only where the plain label is not unique. * * Returns a Map keyed by `key(item)`; a caller that renders from the same list looks each row up as it * draws it. Order is untouched. */ export const distinguishDuplicateOptions = ( items: readonly T[], { key, label, detail }: DistinguishOptions, ): Map => { const counts = new Map(); for (const item of items) { const text = label(item); counts.set(text, (counts.get(text) ?? 0) + 1); } const distinguished = new Map(); for (const item of items) { const text = label(item); const extra = (counts.get(text) ?? 0) > 1 ? detail(item)?.trim() : undefined; distinguished.set(key(item), extra ? `${text} ยท ${extra}` : text); } return distinguished; };