@simplysm/core-common 13.0.100 → 14.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +86 -92
  2. package/dist/common.types.d.ts +14 -14
  3. package/dist/common.types.js +2 -1
  4. package/dist/common.types.js.map +1 -6
  5. package/dist/env.d.ts +8 -1
  6. package/dist/env.d.ts.map +1 -1
  7. package/dist/env.js +13 -9
  8. package/dist/env.js.map +1 -6
  9. package/dist/errors/argument-error.d.ts +10 -10
  10. package/dist/errors/argument-error.d.ts.map +1 -1
  11. package/dist/errors/argument-error.js +31 -14
  12. package/dist/errors/argument-error.js.map +1 -6
  13. package/dist/errors/not-implemented-error.d.ts +8 -8
  14. package/dist/errors/not-implemented-error.js +30 -12
  15. package/dist/errors/not-implemented-error.js.map +1 -6
  16. package/dist/errors/sd-error.d.ts +10 -10
  17. package/dist/errors/sd-error.d.ts.map +1 -1
  18. package/dist/errors/sd-error.js +45 -24
  19. package/dist/errors/sd-error.js.map +1 -6
  20. package/dist/errors/timeout-error.d.ts +10 -10
  21. package/dist/errors/timeout-error.js +34 -15
  22. package/dist/errors/timeout-error.js.map +1 -6
  23. package/dist/extensions/arr-ext.d.ts +2 -2
  24. package/dist/extensions/arr-ext.helpers.d.ts +10 -10
  25. package/dist/extensions/arr-ext.helpers.js +112 -89
  26. package/dist/extensions/arr-ext.helpers.js.map +1 -6
  27. package/dist/extensions/arr-ext.js +458 -422
  28. package/dist/extensions/arr-ext.js.map +1 -6
  29. package/dist/extensions/arr-ext.types.d.ts +57 -57
  30. package/dist/extensions/arr-ext.types.d.ts.map +1 -1
  31. package/dist/extensions/arr-ext.types.js +6 -1
  32. package/dist/extensions/arr-ext.types.js.map +1 -6
  33. package/dist/extensions/map-ext.d.ts +16 -16
  34. package/dist/extensions/map-ext.js +27 -22
  35. package/dist/extensions/map-ext.js.map +1 -6
  36. package/dist/extensions/set-ext.d.ts +11 -11
  37. package/dist/extensions/set-ext.js +32 -25
  38. package/dist/extensions/set-ext.js.map +1 -6
  39. package/dist/features/debounce-queue.d.ts +17 -17
  40. package/dist/features/debounce-queue.js +98 -70
  41. package/dist/features/debounce-queue.js.map +1 -6
  42. package/dist/features/event-emitter.d.ts +20 -20
  43. package/dist/features/event-emitter.js +101 -78
  44. package/dist/features/event-emitter.js.map +1 -6
  45. package/dist/features/serial-queue.d.ts +11 -11
  46. package/dist/features/serial-queue.js +78 -57
  47. package/dist/features/serial-queue.js.map +1 -6
  48. package/dist/globals.d.ts +4 -4
  49. package/dist/globals.js +9 -1
  50. package/dist/globals.js.map +1 -6
  51. package/dist/index.js +28 -27
  52. package/dist/index.js.map +1 -6
  53. package/dist/types/date-only.d.ts +64 -64
  54. package/dist/types/date-only.d.ts.map +1 -1
  55. package/dist/types/date-only.js +263 -252
  56. package/dist/types/date-only.js.map +1 -6
  57. package/dist/types/date-time.d.ts +36 -36
  58. package/dist/types/date-time.d.ts.map +1 -1
  59. package/dist/types/date-time.js +196 -288
  60. package/dist/types/date-time.js.map +1 -6
  61. package/dist/types/lazy-gc-map.d.ts +26 -26
  62. package/dist/types/lazy-gc-map.d.ts.map +1 -1
  63. package/dist/types/lazy-gc-map.js +202 -159
  64. package/dist/types/lazy-gc-map.js.map +1 -6
  65. package/dist/types/time.d.ts +23 -23
  66. package/dist/types/time.d.ts.map +1 -1
  67. package/dist/types/time.js +169 -158
  68. package/dist/types/time.js.map +1 -6
  69. package/dist/types/uuid.d.ts +11 -11
  70. package/dist/types/uuid.d.ts.map +1 -1
  71. package/dist/types/uuid.js +95 -70
  72. package/dist/types/uuid.js.map +1 -6
  73. package/dist/utils/bytes.d.ts +17 -17
  74. package/dist/utils/bytes.js +137 -81
  75. package/dist/utils/bytes.js.map +1 -6
  76. package/dist/utils/date-format.d.ts +40 -40
  77. package/dist/utils/date-format.js +187 -101
  78. package/dist/utils/date-format.js.map +1 -6
  79. package/dist/utils/error.d.ts +4 -4
  80. package/dist/utils/error.js +11 -6
  81. package/dist/utils/error.js.map +1 -6
  82. package/dist/utils/json.d.ts +19 -19
  83. package/dist/utils/json.js +187 -135
  84. package/dist/utils/json.js.map +1 -6
  85. package/dist/utils/num.d.ts +20 -20
  86. package/dist/utils/num.js +76 -34
  87. package/dist/utils/num.js.map +1 -6
  88. package/dist/utils/obj.d.ts +111 -111
  89. package/dist/utils/obj.d.ts.map +1 -1
  90. package/dist/utils/obj.js +706 -496
  91. package/dist/utils/obj.js.map +1 -6
  92. package/dist/utils/path.d.ts +10 -10
  93. package/dist/utils/path.js +35 -18
  94. package/dist/utils/path.js.map +1 -6
  95. package/dist/utils/primitive.d.ts +5 -5
  96. package/dist/utils/primitive.js +34 -14
  97. package/dist/utils/primitive.js.map +1 -6
  98. package/dist/utils/str.d.ts +38 -38
  99. package/dist/utils/str.js +217 -113
  100. package/dist/utils/str.js.map +1 -6
  101. package/dist/utils/template-strings.d.ts +26 -26
  102. package/dist/utils/template-strings.js +113 -40
  103. package/dist/utils/template-strings.js.map +1 -6
  104. package/dist/utils/transferable.d.ts +18 -18
  105. package/dist/utils/transferable.js +218 -151
  106. package/dist/utils/transferable.js.map +1 -6
  107. package/dist/utils/wait.d.ts +9 -9
  108. package/dist/utils/wait.js +30 -15
  109. package/dist/utils/wait.js.map +1 -6
  110. package/dist/utils/xml.d.ts +13 -13
  111. package/dist/utils/xml.js +84 -46
  112. package/dist/utils/xml.js.map +1 -6
  113. package/dist/utils/zip.d.ts +22 -22
  114. package/dist/utils/zip.js +172 -148
  115. package/dist/utils/zip.js.map +1 -6
  116. package/docs/array-extensions.md +430 -0
  117. package/docs/env.md +52 -0
  118. package/docs/errors.md +41 -56
  119. package/docs/features.md +82 -97
  120. package/docs/type-utilities.md +91 -0
  121. package/docs/types.md +221 -201
  122. package/docs/utils.md +319 -435
  123. package/package.json +7 -5
  124. package/src/common.types.ts +14 -14
  125. package/src/env.ts +12 -3
  126. package/src/errors/argument-error.ts +15 -15
  127. package/src/errors/not-implemented-error.ts +9 -9
  128. package/src/errors/sd-error.ts +12 -12
  129. package/src/errors/timeout-error.ts +12 -12
  130. package/src/extensions/arr-ext.helpers.ts +16 -16
  131. package/src/extensions/arr-ext.ts +35 -35
  132. package/src/extensions/arr-ext.types.ts +57 -57
  133. package/src/extensions/map-ext.ts +16 -16
  134. package/src/extensions/set-ext.ts +11 -11
  135. package/src/features/debounce-queue.ts +23 -23
  136. package/src/features/event-emitter.ts +25 -25
  137. package/src/features/serial-queue.ts +13 -13
  138. package/src/globals.ts +4 -4
  139. package/src/index.ts +5 -5
  140. package/src/types/date-only.ts +84 -83
  141. package/src/types/date-time.ts +43 -42
  142. package/src/types/lazy-gc-map.ts +44 -44
  143. package/src/types/time.ts +29 -29
  144. package/src/types/uuid.ts +15 -15
  145. package/src/utils/bytes.ts +35 -35
  146. package/src/utils/date-format.ts +59 -59
  147. package/src/utils/error.ts +4 -4
  148. package/src/utils/json.ts +41 -41
  149. package/src/utils/num.ts +20 -20
  150. package/src/utils/obj.ts +138 -138
  151. package/src/utils/path.ts +10 -10
  152. package/src/utils/primitive.ts +6 -6
  153. package/src/utils/str.ts +48 -48
  154. package/src/utils/template-strings.ts +29 -29
  155. package/src/utils/transferable.ts +38 -38
  156. package/src/utils/wait.ts +10 -10
  157. package/src/utils/xml.ts +19 -19
  158. package/src/utils/zip.ts +25 -25
  159. package/docs/extensions.md +0 -387
  160. package/tests/errors/errors.spec.ts +0 -80
  161. package/tests/extensions/array-extension.spec.ts +0 -654
  162. package/tests/extensions/map-extension.spec.ts +0 -117
  163. package/tests/extensions/set-extension.spec.ts +0 -67
  164. package/tests/types/date-only.spec.ts +0 -533
  165. package/tests/types/date-time.spec.ts +0 -246
  166. package/tests/types/lazy-gc-map.spec.ts +0 -606
  167. package/tests/types/time.spec.ts +0 -428
  168. package/tests/types/uuid.spec.ts +0 -74
  169. package/tests/utils/bytes-utils.spec.ts +0 -197
  170. package/tests/utils/date-format.spec.ts +0 -350
  171. package/tests/utils/debounce-queue.spec.ts +0 -226
  172. package/tests/utils/json.spec.ts +0 -400
  173. package/tests/utils/number.spec.ts +0 -136
  174. package/tests/utils/object.spec.ts +0 -810
  175. package/tests/utils/path.spec.ts +0 -70
  176. package/tests/utils/primitive.spec.ts +0 -43
  177. package/tests/utils/sd-event-emitter.spec.ts +0 -189
  178. package/tests/utils/serial-queue.spec.ts +0 -305
  179. package/tests/utils/string.spec.ts +0 -265
  180. package/tests/utils/template-strings.spec.ts +0 -48
  181. package/tests/utils/transferable.spec.ts +0 -639
  182. package/tests/utils/wait.spec.ts +0 -123
  183. package/tests/utils/xml.spec.ts +0 -146
  184. package/tests/utils/zip.spec.ts +0 -221
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Array 확장 메서드
3
+ *
4
+ * @remarks 각 메서드의 TSDoc은 타입 정의 파일(arr-ext.types.ts)을 참조
5
+ */
1
6
  import "./map-ext.js";
2
7
  import { clone, equal, merge } from "../utils/obj.js";
3
8
  import { DateTime } from "../types/date-time.js";
@@ -7,442 +12,473 @@ import { Uuid } from "../types/uuid.js";
7
12
  import { ArgumentError } from "../errors/argument-error.js";
8
13
  import { SdError } from "../errors/sd-error.js";
9
14
  import { compareForOrder, getDistinctIndices } from "./arr-ext.helpers.js";
15
+ //#region Implementation
10
16
  const arrayReadonlyExtensions = {
11
- single(predicate) {
12
- const arr = predicate !== void 0 ? this.filter(predicate) : this;
13
- if (arr.length > 1) {
14
- throw new ArgumentError("Multiple results found.", { count: arr.length });
15
- }
16
- return arr[0];
17
- },
18
- first(predicate) {
19
- return predicate !== void 0 ? this.find(predicate) : this[0];
20
- },
21
- async filterAsync(predicate) {
22
- const arr = [];
23
- for (let i = 0; i < this.length; i++) {
24
- if (await predicate(this[i], i)) {
25
- arr.push(this[i]);
26
- }
27
- }
28
- return arr;
29
- },
30
- last(predicate) {
31
- if (predicate !== void 0) {
32
- for (let i = this.length - 1; i >= 0; i--) {
33
- if (predicate(this[i], i)) {
34
- return this[i];
17
+ single(predicate) {
18
+ const arr = predicate !== undefined ? this.filter(predicate) : this;
19
+ if (arr.length > 1) {
20
+ throw new ArgumentError("여러 개의 결과가 발견되었습니다.", { count: arr.length });
35
21
  }
36
- }
37
- return void 0;
38
- } else {
39
- return this[this.length - 1];
40
- }
41
- },
42
- filterExists() {
43
- return this.filter((item) => item != null);
44
- },
45
- ofType(type) {
46
- if (typeof type === "string") {
47
- return this.filter((item) => {
48
- switch (type) {
49
- case "string":
50
- return typeof item === "string";
51
- case "number":
52
- return typeof item === "number";
53
- case "boolean":
54
- return typeof item === "boolean";
55
- case "DateTime":
56
- return item instanceof DateTime;
57
- case "DateOnly":
58
- return item instanceof DateOnly;
59
- case "Time":
60
- return item instanceof Time;
61
- case "Uuid":
62
- return item instanceof Uuid;
63
- case "Bytes":
64
- return item instanceof Uint8Array;
65
- default: {
66
- const _exhaustive = type;
67
- throw new ArgumentError(`Unsupported type: ${_exhaustive}`);
68
- }
22
+ return arr[0];
23
+ },
24
+ first(predicate) {
25
+ return predicate !== undefined ? this.find(predicate) : this[0];
26
+ },
27
+ async filterAsync(predicate) {
28
+ const arr = [];
29
+ for (let i = 0; i < this.length; i++) {
30
+ if (await predicate(this[i], i)) {
31
+ arr.push(this[i]);
32
+ }
69
33
  }
70
- });
71
- }
72
- return this.filter((item) => item instanceof type || (item == null ? void 0 : item.constructor) === type);
73
- },
74
- async mapAsync(selector) {
75
- const result = [];
76
- for (let i = 0; i < this.length; i++) {
77
- result.push(await selector(this[i], i));
78
- }
79
- return result;
80
- },
81
- mapMany(selector) {
82
- const arr = selector ? this.map(selector) : this;
83
- return arr.flat().filterExists();
84
- },
85
- async mapManyAsync(selector) {
86
- const arr = selector !== void 0 ? await this.mapAsync(selector) : this;
87
- return arr.mapMany();
88
- },
89
- parallelAsync(fn) {
90
- return Promise.all(this.map(fn));
91
- },
92
- // Group array by key
93
- // Performance considerations:
94
- // - primitive key (string, number, etc.): O(n) - Map-based
95
- // - object key: O(n²) - objEqual comparison
96
- groupBy(keySelector, valueSelector) {
97
- const result = [];
98
- const primitiveKeyIndex = /* @__PURE__ */ new Map();
99
- for (let i = 0; i < this.length; i++) {
100
- const keyObj = keySelector(this[i], i);
101
- const valueObj = valueSelector !== void 0 ? valueSelector(this[i], i) : this[i];
102
- if (keyObj == null || typeof keyObj !== "object") {
103
- const keyStr = typeof keyObj + ":" + String(keyObj);
104
- const existingIndex = primitiveKeyIndex.get(keyStr);
105
- if (existingIndex !== void 0) {
106
- result[existingIndex].values.push(valueObj);
107
- } else {
108
- primitiveKeyIndex.set(keyStr, result.length);
109
- result.push({ key: keyObj, values: [valueObj] });
34
+ return arr;
35
+ },
36
+ last(predicate) {
37
+ if (predicate !== undefined) {
38
+ for (let i = this.length - 1; i >= 0; i--) {
39
+ if (predicate(this[i], i)) {
40
+ return this[i];
41
+ }
42
+ }
43
+ return undefined;
110
44
  }
111
- continue;
112
- }
113
- const existsRecord = result.find((item) => equal(item.key, keyObj));
114
- if (existsRecord !== void 0) {
115
- existsRecord.values.push(valueObj);
116
- } else {
117
- result.push({ key: keyObj, values: [valueObj] });
118
- }
119
- }
120
- return result;
121
- },
122
- toMap(keySelector, valueSelector) {
123
- const result = /* @__PURE__ */ new Map();
124
- for (let i = 0; i < this.length; i++) {
125
- const item = this[i];
126
- const keyObj = keySelector(item, i);
127
- const valueObj = valueSelector !== void 0 ? valueSelector(item, i) : item;
128
- if (result.has(keyObj)) {
129
- throw new ArgumentError("Duplicated key.", { duplicatedKey: keyObj });
130
- }
131
- result.set(keyObj, valueObj);
132
- }
133
- return result;
134
- },
135
- async toMapAsync(keySelector, valueSelector) {
136
- const result = /* @__PURE__ */ new Map();
137
- for (let i = 0; i < this.length; i++) {
138
- const item = this[i];
139
- const keyObj = await keySelector(item, i);
140
- const valueObj = valueSelector !== void 0 ? await valueSelector(item, i) : item;
141
- if (result.has(keyObj)) {
142
- throw new ArgumentError("Duplicated key.", { duplicatedKey: keyObj });
143
- }
144
- result.set(keyObj, valueObj);
145
- }
146
- return result;
147
- },
148
- toArrayMap(keySelector, valueSelector) {
149
- const result = /* @__PURE__ */ new Map();
150
- for (let i = 0; i < this.length; i++) {
151
- const item = this[i];
152
- const keyObj = keySelector(item, i);
153
- const valueObj = valueSelector !== void 0 ? valueSelector(item, i) : item;
154
- const arr = result.getOrCreate(keyObj, []);
155
- arr.push(valueObj);
156
- }
157
- return result;
158
- },
159
- toSetMap(keySelector, valueSelector) {
160
- const result = /* @__PURE__ */ new Map();
161
- for (let i = 0; i < this.length; i++) {
162
- const item = this[i];
163
- const keyObj = keySelector(item, i);
164
- const valueObj = valueSelector !== void 0 ? valueSelector(item, i) : item;
165
- const set = result.getOrCreate(keyObj, /* @__PURE__ */ new Set());
166
- set.add(valueObj);
167
- }
168
- return result;
169
- },
170
- toMapValues(keySelector, valueSelector) {
171
- const itemsMap = /* @__PURE__ */ new Map();
172
- for (let i = 0; i < this.length; i++) {
173
- const item = this[i];
174
- const keyObj = keySelector(item, i);
175
- const arr = itemsMap.getOrCreate(keyObj, []);
176
- arr.push(item);
177
- }
178
- const result = /* @__PURE__ */ new Map();
179
- for (const key of itemsMap.keys()) {
180
- result.set(key, valueSelector(itemsMap.get(key)));
181
- }
182
- return result;
183
- },
184
- toObject(keySelector, valueSelector) {
185
- const result = {};
186
- for (let i = 0; i < this.length; i++) {
187
- const item = this[i];
188
- const key = keySelector(item, i);
189
- const valueObj = valueSelector !== void 0 ? valueSelector(item, i) : item;
190
- if (result[key] !== void 0) {
191
- throw new ArgumentError("Duplicated key.", { duplicatedKey: key });
192
- }
193
- result[key] = valueObj;
194
- }
195
- return result;
196
- },
197
- toTree(key, parentKey) {
198
- const childrenMap = this.toArrayMap((item) => item[parentKey]);
199
- const fn = (items) => {
200
- return items.map((item) => ({
201
- ...clone(item),
202
- children: fn(childrenMap.get(item[key]) ?? [])
203
- }));
204
- };
205
- const rootItems = this.filter((item1) => item1[parentKey] == null);
206
- return fn(rootItems);
207
- },
208
- distinct(options) {
209
- const keptIndices = getDistinctIndices(this, options);
210
- return Array.from(keptIndices).map((i) => this[i]);
211
- },
212
- orderBy(selector) {
213
- return [...this].sort((p, n) => {
214
- const pp = selector == null ? p : selector(p);
215
- const pn = selector == null ? n : selector(n);
216
- return compareForOrder(pp, pn, false);
217
- });
218
- },
219
- orderByDesc(selector) {
220
- return [...this].sort((p, n) => {
221
- const pp = selector == null ? p : selector(p);
222
- const pn = selector == null ? n : selector(n);
223
- return compareForOrder(pp, pn, true);
224
- });
225
- },
226
- diffs(target, options) {
227
- const result = [];
228
- const uncheckedTarget = [...target];
229
- const uncheckedTargetSet = new Set(uncheckedTarget);
230
- const hasKeys = (options == null ? void 0 : options.keys) !== void 0 && options.keys.length > 0;
231
- const excludeOpts = { topLevelExcludes: options == null ? void 0 : options.excludes };
232
- const keyIndexedTarget = hasKeys ? /* @__PURE__ */ new Map() : void 0;
233
- if (keyIndexedTarget) {
234
- for (const targetItem of uncheckedTarget) {
235
- const keyStr = JSON.stringify(
236
- options.keys.map((k) => targetItem[k])
237
- );
238
- const arr = keyIndexedTarget.get(keyStr);
239
- if (arr) {
240
- arr.push(targetItem);
241
- } else {
242
- keyIndexedTarget.set(keyStr, [targetItem]);
45
+ else {
46
+ return this[this.length - 1];
243
47
  }
244
- }
245
- }
246
- for (const sourceItem of this) {
247
- let sameTarget;
248
- let sameKeyTarget;
249
- for (const targetItem of uncheckedTarget) {
250
- if (!uncheckedTargetSet.has(targetItem)) continue;
251
- if (equal(targetItem, sourceItem, excludeOpts)) {
252
- sameTarget = targetItem;
253
- break;
48
+ },
49
+ filterExists() {
50
+ return this.filter((item) => item != null);
51
+ },
52
+ ofType(type) {
53
+ // PrimitiveTypeStr 경우
54
+ if (typeof type === "string") {
55
+ return this.filter((item) => {
56
+ switch (type) {
57
+ case "string":
58
+ return typeof item === "string";
59
+ case "number":
60
+ return typeof item === "number";
61
+ case "boolean":
62
+ return typeof item === "boolean";
63
+ case "DateTime":
64
+ return item instanceof DateTime;
65
+ case "DateOnly":
66
+ return item instanceof DateOnly;
67
+ case "Time":
68
+ return item instanceof Time;
69
+ case "Uuid":
70
+ return item instanceof Uuid;
71
+ case "Bytes":
72
+ return item instanceof Uint8Array;
73
+ default: {
74
+ // 완전성 검사: PrimitiveTypeStr에 새 타입이 추가되면 컴파일 에러 발생
75
+ const _exhaustive = type;
76
+ throw new ArgumentError(`지원하지 않는 타입: ${_exhaustive}`);
77
+ }
78
+ }
79
+ });
254
80
  }
255
- }
256
- if (sameTarget === void 0 && keyIndexedTarget) {
257
- const sourceKeyStr = JSON.stringify(
258
- options.keys.map((k) => sourceItem[k])
259
- );
260
- const candidates = keyIndexedTarget.get(sourceKeyStr);
261
- if (candidates && candidates.length > 0) {
262
- sameKeyTarget = candidates.find((c) => uncheckedTargetSet.has(c));
81
+ // Type<N> (생성자) 경우
82
+ return this.filter((item) => item instanceof type || item?.constructor === type);
83
+ },
84
+ async mapAsync(selector) {
85
+ const result = [];
86
+ for (let i = 0; i < this.length; i++) {
87
+ result.push(await selector(this[i], i));
263
88
  }
264
- }
265
- if (sameTarget !== void 0) {
266
- uncheckedTargetSet.delete(sameTarget);
267
- } else if (sameKeyTarget !== void 0) {
268
- result.push({ source: sourceItem, target: sameKeyTarget });
269
- uncheckedTargetSet.delete(sameKeyTarget);
270
- } else {
271
- result.push({ source: sourceItem, target: void 0 });
272
- }
273
- }
274
- for (const uncheckedTargetItem of uncheckedTargetSet) {
275
- result.push({ source: void 0, target: uncheckedTargetItem });
276
- }
277
- return result;
278
- },
279
- oneWayDiffs(orgItems, keyPropNameOrGetValFn, options) {
280
- const orgItemMap = orgItems instanceof Map ? orgItems : orgItems.toMap(
281
- (orgItem) => typeof keyPropNameOrGetValFn === "function" ? keyPropNameOrGetValFn(orgItem) : orgItem[keyPropNameOrGetValFn]
282
- );
283
- const includeSame = (options == null ? void 0 : options.includeSame) ?? false;
284
- const diffs = [];
285
- for (const item of this) {
286
- const keyValue = typeof keyPropNameOrGetValFn === "function" ? keyPropNameOrGetValFn(item) : item[keyPropNameOrGetValFn];
287
- if (keyValue == null) {
288
- diffs.push({ type: "create", item, orgItem: void 0 });
289
- continue;
290
- }
291
- const orgItem = orgItemMap.get(keyValue);
292
- if (!orgItem) {
293
- diffs.push({ type: "create", item, orgItem: void 0 });
294
- continue;
295
- }
296
- if (equal(orgItem, item, {
297
- topLevelExcludes: options == null ? void 0 : options.excludes,
298
- topLevelIncludes: options == null ? void 0 : options.includes
299
- })) {
300
- if (includeSame) {
301
- diffs.push({ type: "same", item, orgItem });
89
+ return result;
90
+ },
91
+ mapMany(selector) {
92
+ const arr = selector ? this.map(selector) : this;
93
+ return arr.flat().filterExists();
94
+ },
95
+ async mapManyAsync(selector) {
96
+ const arr = selector !== undefined ? await this.mapAsync(selector) : this;
97
+ return arr.mapMany();
98
+ },
99
+ parallelAsync(fn) {
100
+ return Promise.all(this.map(fn));
101
+ },
102
+ // key 기준으로 array 그룹화
103
+ // 성능 고려사항:
104
+ // - 원시 key (string, number ): O(n) - Map 기반
105
+ // - 객체 key: O(n²) - objEqual 비교
106
+ groupBy(keySelector, valueSelector) {
107
+ const result = [];
108
+ // 원시 key 최적화를 위한 Map (key 문자열 -> result index)
109
+ const primitiveKeyIndex = new Map();
110
+ for (let i = 0; i < this.length; i++) {
111
+ const keyObj = keySelector(this[i], i);
112
+ const valueObj = valueSelector !== undefined ? valueSelector(this[i], i) : this[i];
113
+ // 원시 key는 Map을 사용하여 O(n)으로 처리
114
+ if (keyObj == null || typeof keyObj !== "object") {
115
+ const keyStr = typeof keyObj + ":" + String(keyObj);
116
+ const existingIndex = primitiveKeyIndex.get(keyStr);
117
+ if (existingIndex !== undefined) {
118
+ result[existingIndex].values.push(valueObj);
119
+ }
120
+ else {
121
+ primitiveKeyIndex.set(keyStr, result.length);
122
+ result.push({ key: keyObj, values: [valueObj] });
123
+ }
124
+ continue;
125
+ }
126
+ // 객체 key는 기존 방식 O(n²) 사용
127
+ const existsRecord = result.find((item) => equal(item.key, keyObj));
128
+ if (existsRecord !== undefined) {
129
+ existsRecord.values.push(valueObj);
130
+ }
131
+ else {
132
+ result.push({ key: keyObj, values: [valueObj] });
133
+ }
302
134
  }
303
- continue;
304
- }
305
- diffs.push({ type: "update", item, orgItem });
306
- }
307
- return diffs;
308
- },
309
- merge(target, options) {
310
- const diffs = this.diffs(target, options);
311
- const result = clone(this);
312
- const sourceIndexMap = /* @__PURE__ */ new Map();
313
- for (let i = 0; i < this.length; i++) {
314
- sourceIndexMap.set(this[i], i);
315
- }
316
- for (const diff of diffs) {
317
- if (diff.source !== void 0 && diff.target !== void 0) {
318
- const sourceIndex = sourceIndexMap.get(diff.source);
319
- if (sourceIndex === void 0) {
320
- throw new SdError("Unexpected error: source item not found in merge.");
135
+ return result;
136
+ },
137
+ toMap(keySelector, valueSelector) {
138
+ const result = new Map();
139
+ for (let i = 0; i < this.length; i++) {
140
+ const item = this[i];
141
+ const keyObj = keySelector(item, i);
142
+ const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
143
+ if (result.has(keyObj)) {
144
+ throw new ArgumentError("중복된 key입니다.", { duplicatedKey: keyObj });
145
+ }
146
+ result.set(keyObj, valueObj);
321
147
  }
322
- result[sourceIndex] = merge(diff.source, diff.target);
323
- } else if (diff.target !== void 0) {
324
- result.push(diff.target);
325
- }
326
- }
327
- return result;
328
- },
329
- sum(selector) {
330
- let result = 0;
331
- for (let i = 0; i < this.length; i++) {
332
- const item = selector !== void 0 ? selector(this[i], i) : this[i];
333
- if (typeof item !== "number") {
334
- throw new ArgumentError("sum can only be used with numbers.", {
335
- type: typeof item
336
- });
337
- }
338
- result += item;
339
- }
340
- return result;
341
- },
342
- min(selector) {
343
- let result;
344
- for (let i = 0; i < this.length; i++) {
345
- const item = selector !== void 0 ? selector(this[i], i) : this[i];
346
- if (typeof item !== "number" && typeof item !== "string") {
347
- throw new ArgumentError("min can only be used with numbers/strings.", {
348
- type: typeof item
148
+ return result;
149
+ },
150
+ async toMapAsync(keySelector, valueSelector) {
151
+ const result = new Map();
152
+ for (let i = 0; i < this.length; i++) {
153
+ const item = this[i];
154
+ const keyObj = await keySelector(item, i);
155
+ const valueObj = valueSelector !== undefined ? await valueSelector(item, i) : item;
156
+ if (result.has(keyObj)) {
157
+ throw new ArgumentError("중복된 key입니다.", { duplicatedKey: keyObj });
158
+ }
159
+ result.set(keyObj, valueObj);
160
+ }
161
+ return result;
162
+ },
163
+ toArrayMap(keySelector, valueSelector) {
164
+ const result = new Map();
165
+ for (let i = 0; i < this.length; i++) {
166
+ const item = this[i];
167
+ const keyObj = keySelector(item, i);
168
+ const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
169
+ const arr = result.getOrCreate(keyObj, []);
170
+ arr.push(valueObj);
171
+ }
172
+ return result;
173
+ },
174
+ toSetMap(keySelector, valueSelector) {
175
+ const result = new Map();
176
+ for (let i = 0; i < this.length; i++) {
177
+ const item = this[i];
178
+ const keyObj = keySelector(item, i);
179
+ const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
180
+ const set = result.getOrCreate(keyObj, new Set());
181
+ set.add(valueObj);
182
+ }
183
+ return result;
184
+ },
185
+ toMapValues(keySelector, valueSelector) {
186
+ const itemsMap = new Map();
187
+ for (let i = 0; i < this.length; i++) {
188
+ const item = this[i];
189
+ const keyObj = keySelector(item, i);
190
+ const arr = itemsMap.getOrCreate(keyObj, []);
191
+ arr.push(item);
192
+ }
193
+ const result = new Map();
194
+ for (const key of itemsMap.keys()) {
195
+ result.set(key, valueSelector(itemsMap.get(key)));
196
+ }
197
+ return result;
198
+ },
199
+ toObject(keySelector, valueSelector) {
200
+ const result = {};
201
+ for (let i = 0; i < this.length; i++) {
202
+ const item = this[i];
203
+ const key = keySelector(item, i);
204
+ const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
205
+ // undefined 값은 "없음"으로 처리하여 덮어쓰기 허용
206
+ if (result[key] !== undefined) {
207
+ throw new ArgumentError("중복된 key입니다.", { duplicatedKey: key });
208
+ }
209
+ result[key] = valueObj;
210
+ }
211
+ return result;
212
+ },
213
+ toTree(key, parentKey) {
214
+ // O(n) 최적화: Map 기반 인덱싱
215
+ const childrenMap = this.toArrayMap((item) => item[parentKey]);
216
+ const fn = (items) => {
217
+ return items.map((item) => ({
218
+ ...clone(item),
219
+ children: fn(childrenMap.get(item[key]) ?? []),
220
+ }));
221
+ };
222
+ const rootItems = this.filter((item1) => item1[parentKey] == null);
223
+ return fn(rootItems);
224
+ },
225
+ distinct(options) {
226
+ const keptIndices = getDistinctIndices(this, options);
227
+ return Array.from(keptIndices).map((i) => this[i]);
228
+ },
229
+ orderBy(selector) {
230
+ return [...this].sort((p, n) => {
231
+ const pp = selector == null ? p : selector(p);
232
+ const pn = selector == null ? n : selector(n);
233
+ return compareForOrder(pp, pn, false);
349
234
  });
350
- }
351
- if (result === void 0 || result > item) {
352
- result = item;
353
- }
354
- }
355
- return result;
356
- },
357
- max(selector) {
358
- let result;
359
- for (let i = 0; i < this.length; i++) {
360
- const item = selector !== void 0 ? selector(this[i], i) : this[i];
361
- if (typeof item !== "number" && typeof item !== "string") {
362
- throw new ArgumentError("max can only be used with numbers/strings.", {
363
- type: typeof item
235
+ },
236
+ orderByDesc(selector) {
237
+ return [...this].sort((p, n) => {
238
+ const pp = selector == null ? p : selector(p);
239
+ const pn = selector == null ? n : selector(n);
240
+ return compareForOrder(pp, pn, true);
364
241
  });
365
- }
366
- if (result === void 0 || result < item) {
367
- result = item;
368
- }
369
- }
370
- return result;
371
- },
372
- shuffle() {
373
- if (this.length <= 1) {
374
- return [...this];
375
- }
376
- const result = [...this];
377
- for (let i = result.length - 1; i > 0; i--) {
378
- const j = Math.floor(Math.random() * (i + 1));
379
- [result[i], result[j]] = [result[j], result[i]];
380
- }
381
- return result;
382
- }
242
+ },
243
+ diffs(target, options) {
244
+ const result = [];
245
+ const uncheckedTarget = [...target];
246
+ const uncheckedTargetSet = new Set(uncheckedTarget);
247
+ const hasKeys = options?.keys !== undefined && options.keys.length > 0;
248
+ const excludeOpts = { topLevelExcludes: options?.excludes };
249
+ // keys 옵션이 제공되면 target을 Map으로 사전 인덱싱하여 O(n×m) → O(n+m)으로 개선
250
+ // 같은 key 값을 가진 target이 여러 개 존재할 수 있으므로 array로 저장
251
+ const keyIndexedTarget = hasKeys ? new Map() : undefined;
252
+ if (keyIndexedTarget) {
253
+ for (const targetItem of uncheckedTarget) {
254
+ const keyStr = JSON.stringify(options.keys.map((k) => targetItem[k]));
255
+ const arr = keyIndexedTarget.get(keyStr);
256
+ if (arr) {
257
+ arr.push(targetItem);
258
+ }
259
+ else {
260
+ keyIndexedTarget.set(keyStr, [targetItem]);
261
+ }
262
+ }
263
+ }
264
+ for (const sourceItem of this) {
265
+ // 전체 일치(sameTarget)를 우선하고, 없으면 key 일치(sameKeyTarget)를 검색
266
+ let sameTarget;
267
+ let sameKeyTarget;
268
+ // Set 기반 건너뛰기로 이미 매칭된 항목 스킵 (O(n) splice 제거 방지)
269
+ for (const targetItem of uncheckedTarget) {
270
+ if (!uncheckedTargetSet.has(targetItem))
271
+ continue;
272
+ if (equal(targetItem, sourceItem, excludeOpts)) {
273
+ sameTarget = targetItem;
274
+ break;
275
+ }
276
+ }
277
+ // 전체 일치가 없고 keys 옵션이 있으면 Map에서 O(1) 조회 수행
278
+ if (sameTarget === undefined && keyIndexedTarget) {
279
+ const sourceKeyStr = JSON.stringify(options.keys.map((k) => sourceItem[k]));
280
+ const candidates = keyIndexedTarget.get(sourceKeyStr);
281
+ if (candidates && candidates.length > 0) {
282
+ // uncheckedTargetSet에서 O(1) 조회로 남은 첫 번째 항목 선택
283
+ sameKeyTarget = candidates.find((c) => uncheckedTargetSet.has(c));
284
+ }
285
+ }
286
+ if (sameTarget !== undefined) {
287
+ uncheckedTargetSet.delete(sameTarget);
288
+ }
289
+ else if (sameKeyTarget !== undefined) {
290
+ result.push({ source: sourceItem, target: sameKeyTarget });
291
+ uncheckedTargetSet.delete(sameKeyTarget);
292
+ }
293
+ else {
294
+ result.push({ source: sourceItem, target: undefined });
295
+ }
296
+ }
297
+ for (const uncheckedTargetItem of uncheckedTargetSet) {
298
+ result.push({ source: undefined, target: uncheckedTargetItem });
299
+ }
300
+ return result;
301
+ },
302
+ oneWayDiffs(orgItems, keyPropNameOrGetValFn, options) {
303
+ const orgItemMap = orgItems instanceof Map
304
+ ? orgItems
305
+ : orgItems.toMap((orgItem) => typeof keyPropNameOrGetValFn === "function"
306
+ ? keyPropNameOrGetValFn(orgItem)
307
+ : orgItem[keyPropNameOrGetValFn]);
308
+ const includeSame = options?.includeSame ?? false;
309
+ const diffs = [];
310
+ for (const item of this) {
311
+ const keyValue = typeof keyPropNameOrGetValFn === "function"
312
+ ? keyPropNameOrGetValFn(item)
313
+ : item[keyPropNameOrGetValFn];
314
+ if (keyValue == null) {
315
+ diffs.push({ type: "create", item, orgItem: undefined });
316
+ continue;
317
+ }
318
+ const orgItem = orgItemMap.get(keyValue);
319
+ if (!orgItem) {
320
+ diffs.push({ type: "create", item, orgItem: undefined });
321
+ continue;
322
+ }
323
+ if (equal(orgItem, item, {
324
+ topLevelExcludes: options?.excludes,
325
+ topLevelIncludes: options?.includes,
326
+ })) {
327
+ if (includeSame) {
328
+ diffs.push({ type: "same", item, orgItem });
329
+ }
330
+ continue;
331
+ }
332
+ diffs.push({ type: "update", item, orgItem });
333
+ }
334
+ return diffs;
335
+ },
336
+ merge(target, options) {
337
+ const diffs = this.diffs(target, options);
338
+ const result = clone(this);
339
+ // source 항목의 원래 index를 사전 계산하여 O(n) 조회를 O(1)로 개선
340
+ const sourceIndexMap = new Map();
341
+ for (let i = 0; i < this.length; i++) {
342
+ sourceIndexMap.set(this[i], i);
343
+ }
344
+ for (const diff of diffs) {
345
+ // 업데이트 시
346
+ if (diff.source !== undefined && diff.target !== undefined) {
347
+ const sourceIndex = sourceIndexMap.get(diff.source);
348
+ if (sourceIndex === undefined) {
349
+ throw new SdError("예상치 못한 오류: merge에서 source 항목을 찾을 수 없습니다.");
350
+ }
351
+ result[sourceIndex] = merge(diff.source, diff.target);
352
+ }
353
+ // 추가 시
354
+ else if (diff.target !== undefined) {
355
+ result.push(diff.target);
356
+ }
357
+ }
358
+ return result;
359
+ },
360
+ sum(selector) {
361
+ let result = 0;
362
+ for (let i = 0; i < this.length; i++) {
363
+ const item = selector !== undefined ? selector(this[i], i) : this[i];
364
+ if (typeof item !== "number") {
365
+ throw new ArgumentError("sum은 숫자에만 사용할 수 있습니다.", {
366
+ type: typeof item,
367
+ });
368
+ }
369
+ result += item;
370
+ }
371
+ return result;
372
+ },
373
+ min(selector) {
374
+ let result;
375
+ for (let i = 0; i < this.length; i++) {
376
+ const item = selector !== undefined ? selector(this[i], i) : this[i];
377
+ if (typeof item !== "number" && typeof item !== "string") {
378
+ throw new ArgumentError("min은 숫자/문자열에만 사용할 수 있습니다.", {
379
+ type: typeof item,
380
+ });
381
+ }
382
+ if (result === undefined || result > item) {
383
+ result = item;
384
+ }
385
+ }
386
+ return result;
387
+ },
388
+ max(selector) {
389
+ let result;
390
+ for (let i = 0; i < this.length; i++) {
391
+ const item = selector !== undefined ? selector(this[i], i) : this[i];
392
+ if (typeof item !== "number" && typeof item !== "string") {
393
+ throw new ArgumentError("max는 숫자/문자열에만 사용할 수 있습니다.", {
394
+ type: typeof item,
395
+ });
396
+ }
397
+ if (result === undefined || result < item) {
398
+ result = item;
399
+ }
400
+ }
401
+ return result;
402
+ },
403
+ shuffle() {
404
+ if (this.length <= 1) {
405
+ return [...this];
406
+ }
407
+ const result = [...this];
408
+ for (let i = result.length - 1; i > 0; i--) {
409
+ const j = Math.floor(Math.random() * (i + 1));
410
+ [result[i], result[j]] = [result[j], result[i]];
411
+ }
412
+ return result;
413
+ },
383
414
  };
384
415
  const arrayMutableExtensions = {
385
- distinctThis(options) {
386
- const keptIndices = getDistinctIndices(this, options);
387
- const indicesToRemove = [];
388
- for (let i = 0; i < this.length; i++) {
389
- if (!keptIndices.has(i)) {
390
- indicesToRemove.push(i);
391
- }
392
- }
393
- for (let i = indicesToRemove.length - 1; i >= 0; i--) {
394
- this.splice(indicesToRemove[i], 1);
395
- }
396
- return this;
397
- },
398
- orderByThis(selector) {
399
- return this.sort((p, n) => {
400
- const pp = (selector == null ? void 0 : selector(p)) ?? p;
401
- const pn = (selector == null ? void 0 : selector(n)) ?? n;
402
- return compareForOrder(pp, pn, false);
403
- });
404
- },
405
- orderByDescThis(selector) {
406
- return this.sort((p, n) => {
407
- const pp = (selector == null ? void 0 : selector(p)) ?? p;
408
- const pn = (selector == null ? void 0 : selector(n)) ?? n;
409
- return compareForOrder(pp, pn, true);
410
- });
411
- },
412
- insert(index, ...items) {
413
- this.splice(index, 0, ...items);
414
- return this;
415
- },
416
- remove(itemOrSelector) {
417
- const shouldRemove = typeof itemOrSelector === "function" ? itemOrSelector : (item) => item === itemOrSelector;
418
- for (let i = this.length - 1; i >= 0; i--) {
419
- if (shouldRemove(this[i], i)) {
420
- this.splice(i, 1);
421
- }
422
- }
423
- return this;
424
- },
425
- toggle(item) {
426
- if (this.includes(item)) {
427
- this.remove(item);
428
- } else {
429
- this.push(item);
430
- }
431
- return this;
432
- },
433
- clear() {
434
- return this.remove(() => true);
435
- }
416
+ distinctThis(options) {
417
+ const keptIndices = getDistinctIndices(this, options);
418
+ const indicesToRemove = [];
419
+ for (let i = 0; i < this.length; i++) {
420
+ if (!keptIndices.has(i)) {
421
+ indicesToRemove.push(i);
422
+ }
423
+ }
424
+ // index 변동을 방지하기 위해 역순으로 제거
425
+ for (let i = indicesToRemove.length - 1; i >= 0; i--) {
426
+ this.splice(indicesToRemove[i], 1);
427
+ }
428
+ return this;
429
+ },
430
+ orderByThis(selector) {
431
+ return this.sort((p, n) => {
432
+ const pp = selector?.(p) ?? p;
433
+ const pn = selector?.(n) ?? n;
434
+ return compareForOrder(pp, pn, false);
435
+ });
436
+ },
437
+ orderByDescThis(selector) {
438
+ return this.sort((p, n) => {
439
+ const pp = selector?.(p) ?? p;
440
+ const pn = selector?.(n) ?? n;
441
+ return compareForOrder(pp, pn, true);
442
+ });
443
+ },
444
+ insert(index, ...items) {
445
+ this.splice(index, 0, ...items);
446
+ return this;
447
+ },
448
+ remove(itemOrSelector) {
449
+ const shouldRemove = typeof itemOrSelector === "function"
450
+ ? itemOrSelector
451
+ : (item) => item === itemOrSelector;
452
+ // index 변경 문제를 방지하기 위한 역순 순회 (O(n) 성능)
453
+ for (let i = this.length - 1; i >= 0; i--) {
454
+ if (shouldRemove(this[i], i)) {
455
+ this.splice(i, 1);
456
+ }
457
+ }
458
+ return this;
459
+ },
460
+ toggle(item) {
461
+ if (this.includes(item)) {
462
+ this.remove(item);
463
+ }
464
+ else {
465
+ this.push(item);
466
+ }
467
+ return this;
468
+ },
469
+ clear() {
470
+ return this.remove(() => true);
471
+ },
436
472
  };
437
473
  for (const [name, fn] of Object.entries({
438
- ...arrayReadonlyExtensions,
439
- ...arrayMutableExtensions
474
+ ...arrayReadonlyExtensions,
475
+ ...arrayMutableExtensions,
440
476
  })) {
441
- Object.defineProperty(Array.prototype, name, {
442
- value: fn,
443
- enumerable: false,
444
- writable: true,
445
- configurable: true
446
- });
477
+ Object.defineProperty(Array.prototype, name, {
478
+ value: fn,
479
+ enumerable: false,
480
+ writable: true,
481
+ configurable: true,
482
+ });
447
483
  }
448
- //# sourceMappingURL=arr-ext.js.map
484
+ //# sourceMappingURL=arr-ext.js.map