import type { DataRawDump } from "../factorio-process-data.models.ts"; /** * Compare two prototype objects by their `order` field. * If `order` is missing, it defaults to an empty string. * * @param a - First prototype object. * @param b - Second prototype object. * @returns Negative if `a.order < b.order`, positive if greater, zero otherwise. */ function orderFactorioPrototype(a: { order?: string }, b: { order?: string }) { return (a.order ?? "")?.localeCompare(b.order ?? ""); } export type SubGroup = { name: string; order?: string; children: Array; }; export type Group = { name: string; order?: string; icon?: string; subGroup: Array>; }; /** Type of an object that can be grouped by its `subgroup` property. */ export interface GroupablaPrototype { subgroup: string; order?: string; } /** * Create a map of subgroup names to their corresponding `SubGroup` objects. * * @param prototypes - The prototypes to group. * @param subGroupsDefinitions - Definition objects for the available subgroups. * @returns Map where the key is the subgroup name and the value is a `SubGroup`. */ function createSubGroupMap( prototypes: Array, subGroupsDefinitions: Array<{ name: string; order?: string }> ): Map> { const map = new Map>(); for (const proto of prototypes) { if (!map.has(proto.subgroup)) { const subGroup = subGroupsDefinitions.find((sg) => sg.name === proto.subgroup); if (subGroup) { map.set(proto.subgroup, { name: subGroup.name, order: subGroup.order ?? "", children: [], }); } } const subgroup = map.get(proto.subgroup); subgroup?.children.push(proto); } return map; } /** * Create a map of group names to their corresponding `Group` objects. * * @param subGroupMap - Map of subgroup names to `SubGroup` objects. * @param groupsDef - Definition objects for the available groups. * @param subGroupsDef - Definition objects for the available subgroups. * @returns Map where the key is the group name and the value is a `Group`. */ function createGroupMap( subGroupMap: Map>, groupsDef: Array<{ name: string; order?: string }>, subGroupsDef: Array<{ name: string; group: string; order?: string }> ): Map> { const map = new Map>(); for (const subGroupDef of subGroupsDef) { const subGroup = subGroupMap.get(subGroupDef.name); if (!subGroup || subGroup.children.length === 0) continue; if (!map.has(subGroupDef.group)) { const groupDef = groupsDef.find((g) => g.name === subGroupDef.group); if (groupDef) { map.set(subGroupDef.group, { name: groupDef.name, icon: `item-group/${groupDef.name}.png`, order: groupDef.order, subGroup: [], }); } } map.get(subGroupDef.group)?.subGroup.push(subGroup); } return map; } /** * Sort groups, subgroups, and their children by the `order` field. * * @param groups - Array of `Group` objects to be sorted. */ function sortGroups(groups: Group[]) { groups.sort(orderFactorioPrototype); for (const group of groups) { group.subGroup.sort(orderFactorioPrototype); for (const subGroup of group.subGroup) { subGroup.children.sort(orderFactorioPrototype); } } } /** * Remove the `order` property from all groups, subgroups, and prototypes. * * @param groups - Array of `Group` objects from which to delete `order`. */ function stripOrder(groups: Group[]) { for (const group of groups) { delete group.order; group.subGroup.sort(orderFactorioPrototype); for (const subGroup of group.subGroup) { delete subGroup.order; for (const child of subGroup.children) { delete child.order; } } } } /** * Group the factorio prototypes according to there subgroups * @param arr the array of item to be grouped * @param data the rawData object extracted from Factorio * @param keepOrder flag to indicate if the order values must be kept in the final output * @returns */ export function groupPrototypes( arr: Array, data: DataRawDump, keepOrder: boolean = true ): Group[] { const itemGroups = Object.values(data["item-group"]).filter((o) => !o.hidden); const itemSubGroups = Object.values(data["item-subgroup"]).filter((o) => !o.hidden); const subGroupMap = createSubGroupMap(arr, itemSubGroups); const groupMap = createGroupMap(subGroupMap, itemGroups, itemSubGroups); const output = Array.from(groupMap.values()); // Sort the output sortGroups(output); if (!keepOrder) { stripOrder(output); } return output; }