groups.helper.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import type { DataRawDump } from "../factorio-process-data.models.ts";
  2. /**
  3. * Compare two prototype objects by their `order` field.
  4. * If `order` is missing, it defaults to an empty string.
  5. *
  6. * @param a - First prototype object.
  7. * @param b - Second prototype object.
  8. * @returns Negative if `a.order < b.order`, positive if greater, zero otherwise.
  9. */
  10. function orderFactorioPrototype(a: { order?: string }, b: { order?: string }) {
  11. return (a.order ?? "")?.localeCompare(b.order ?? "");
  12. }
  13. export type SubGroup<T> = {
  14. name: string;
  15. order?: string;
  16. children: Array<T>;
  17. };
  18. export type Group<T> = {
  19. name: string;
  20. order?: string;
  21. icon?: string;
  22. subGroup: Array<SubGroup<T>>;
  23. };
  24. /** Type of an object that can be grouped by its `subgroup` property. */
  25. export interface GroupablaPrototype {
  26. subgroup: string;
  27. order?: string;
  28. }
  29. /**
  30. * Create a map of subgroup names to their corresponding `SubGroup` objects.
  31. *
  32. * @param prototypes - The prototypes to group.
  33. * @param subGroupsDefinitions - Definition objects for the available subgroups.
  34. * @returns Map where the key is the subgroup name and the value is a `SubGroup`.
  35. */
  36. function createSubGroupMap<T extends GroupablaPrototype>(
  37. prototypes: Array<T>,
  38. subGroupsDefinitions: Array<{ name: string; order?: string }>
  39. ): Map<string, SubGroup<T>> {
  40. const map = new Map<string, SubGroup<T>>();
  41. for (const proto of prototypes) {
  42. if (!map.has(proto.subgroup)) {
  43. const subGroup = subGroupsDefinitions.find((sg) => sg.name === proto.subgroup);
  44. if (subGroup) {
  45. map.set(proto.subgroup, {
  46. name: subGroup.name,
  47. order: subGroup.order ?? "",
  48. children: [],
  49. });
  50. }
  51. }
  52. const subgroup = map.get(proto.subgroup);
  53. subgroup?.children.push(proto);
  54. }
  55. return map;
  56. }
  57. /**
  58. * Create a map of group names to their corresponding `Group` objects.
  59. *
  60. * @param subGroupMap - Map of subgroup names to `SubGroup` objects.
  61. * @param groupsDef - Definition objects for the available groups.
  62. * @param subGroupsDef - Definition objects for the available subgroups.
  63. * @returns Map where the key is the group name and the value is a `Group`.
  64. */
  65. function createGroupMap<T extends GroupablaPrototype>(
  66. subGroupMap: Map<string, SubGroup<T>>,
  67. groupsDef: Array<{ name: string; order?: string }>,
  68. subGroupsDef: Array<{ name: string; group: string; order?: string }>
  69. ): Map<string, Group<T>> {
  70. const map = new Map<string, Group<T>>();
  71. for (const subGroupDef of subGroupsDef) {
  72. const subGroup = subGroupMap.get(subGroupDef.name);
  73. if (!subGroup || subGroup.children.length === 0) continue;
  74. if (!map.has(subGroupDef.group)) {
  75. const groupDef = groupsDef.find((g) => g.name === subGroupDef.group);
  76. if (groupDef) {
  77. map.set(subGroupDef.group, {
  78. name: groupDef.name,
  79. icon: `item-group/${groupDef.name}.png`,
  80. order: groupDef.order,
  81. subGroup: [],
  82. });
  83. }
  84. }
  85. map.get(subGroupDef.group)?.subGroup.push(subGroup);
  86. }
  87. return map;
  88. }
  89. /**
  90. * Sort groups, subgroups, and their children by the `order` field.
  91. *
  92. * @param groups - Array of `Group` objects to be sorted.
  93. */
  94. function sortGroups<T extends GroupablaPrototype>(groups: Group<T>[]) {
  95. groups.sort(orderFactorioPrototype);
  96. for (const group of groups) {
  97. group.subGroup.sort(orderFactorioPrototype);
  98. for (const subGroup of group.subGroup) {
  99. subGroup.children.sort(orderFactorioPrototype);
  100. }
  101. }
  102. }
  103. /**
  104. * Remove the `order` property from all groups, subgroups, and prototypes.
  105. *
  106. * @param groups - Array of `Group` objects from which to delete `order`.
  107. */
  108. function stripOrder<T extends GroupablaPrototype>(groups: Group<T>[]) {
  109. for (const group of groups) {
  110. delete group.order;
  111. group.subGroup.sort(orderFactorioPrototype);
  112. for (const subGroup of group.subGroup) {
  113. delete subGroup.order;
  114. for (const child of subGroup.children) {
  115. delete child.order;
  116. }
  117. }
  118. }
  119. }
  120. /**
  121. * Group the factorio prototypes according to there subgroups
  122. * @param arr the array of item to be grouped
  123. * @param data the rawData object extracted from Factorio
  124. * @param keepOrder flag to indicate if the order values must be kept in the final output
  125. * @returns
  126. */
  127. export function groupPrototypes<T extends GroupablaPrototype>(
  128. arr: Array<T>,
  129. data: DataRawDump,
  130. keepOrder: boolean = true
  131. ): Group<T>[] {
  132. const itemGroups = Object.values(data["item-group"]).filter((o) => !o.hidden);
  133. const itemSubGroups = Object.values(data["item-subgroup"]).filter((o) => !o.hidden);
  134. const subGroupMap = createSubGroupMap(arr, itemSubGroups);
  135. const groupMap = createGroupMap(subGroupMap, itemGroups, itemSubGroups);
  136. const output = Array.from(groupMap.values());
  137. // Sort the output
  138. sortGroups(output);
  139. if (!keepOrder) {
  140. stripOrder(output);
  141. }
  142. return output;
  143. }