48 lines
1.0 KiB
TypeScript
48 lines
1.0 KiB
TypeScript
type GroupedItem<T>
|
|
= | { item: T; type: "single" }
|
|
| { items: [T, T, ...T[]]; key: string; type: "group" };
|
|
|
|
function groupItems<T>(
|
|
items: readonly T[],
|
|
keyOf: (item: T) => null | string,
|
|
): GroupedItem<T>[] {
|
|
const result: GroupedItem<T>[] = [];
|
|
const indices = new Map<string, number>();
|
|
|
|
for (const item of items) {
|
|
const key = keyOf(item);
|
|
|
|
if (key === null) {
|
|
result.push({ item, type: "single" });
|
|
continue;
|
|
}
|
|
|
|
const index = indices.get(key);
|
|
|
|
if (index === undefined) {
|
|
indices.set(key, result.length);
|
|
result.push({ item, type: "single" });
|
|
continue;
|
|
}
|
|
|
|
// eslint-disable-next-line security/detect-object-injection -- Map-owned index.
|
|
const existing = result[index];
|
|
|
|
if (existing?.type === "single") {
|
|
// eslint-disable-next-line security/detect-object-injection -- Map-owned index.
|
|
result[index] = {
|
|
items: [existing.item, item],
|
|
key,
|
|
type: "group",
|
|
};
|
|
}
|
|
else if (existing) {
|
|
existing.items.push(item);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export { groupItems };
|