@simplysm/core-common 13.0.99 → 14.0.1
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.
- package/dist/common.types.d.ts +14 -14
- package/dist/common.types.js +2 -1
- package/dist/common.types.js.map +1 -6
- package/dist/env.d.ts +5 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +12 -8
- package/dist/env.js.map +1 -6
- package/dist/errors/argument-error.d.ts +10 -10
- package/dist/errors/argument-error.d.ts.map +1 -1
- package/dist/errors/argument-error.js +31 -14
- package/dist/errors/argument-error.js.map +1 -6
- package/dist/errors/not-implemented-error.d.ts +8 -8
- package/dist/errors/not-implemented-error.js +30 -12
- package/dist/errors/not-implemented-error.js.map +1 -6
- package/dist/errors/sd-error.d.ts +10 -10
- package/dist/errors/sd-error.d.ts.map +1 -1
- package/dist/errors/sd-error.js +45 -24
- package/dist/errors/sd-error.js.map +1 -6
- package/dist/errors/timeout-error.d.ts +10 -10
- package/dist/errors/timeout-error.js +34 -15
- package/dist/errors/timeout-error.js.map +1 -6
- package/dist/extensions/arr-ext.d.ts +2 -2
- package/dist/extensions/arr-ext.helpers.d.ts +10 -10
- package/dist/extensions/arr-ext.helpers.js +112 -89
- package/dist/extensions/arr-ext.helpers.js.map +1 -6
- package/dist/extensions/arr-ext.js +458 -422
- package/dist/extensions/arr-ext.js.map +1 -6
- package/dist/extensions/arr-ext.types.d.ts +57 -57
- package/dist/extensions/arr-ext.types.d.ts.map +1 -1
- package/dist/extensions/arr-ext.types.js +6 -1
- package/dist/extensions/arr-ext.types.js.map +1 -6
- package/dist/extensions/map-ext.d.ts +16 -16
- package/dist/extensions/map-ext.js +27 -22
- package/dist/extensions/map-ext.js.map +1 -6
- package/dist/extensions/set-ext.d.ts +11 -11
- package/dist/extensions/set-ext.js +32 -25
- package/dist/extensions/set-ext.js.map +1 -6
- package/dist/features/debounce-queue.d.ts +17 -17
- package/dist/features/debounce-queue.js +98 -70
- package/dist/features/debounce-queue.js.map +1 -6
- package/dist/features/event-emitter.d.ts +20 -20
- package/dist/features/event-emitter.js +101 -78
- package/dist/features/event-emitter.js.map +1 -6
- package/dist/features/serial-queue.d.ts +11 -11
- package/dist/features/serial-queue.js +78 -57
- package/dist/features/serial-queue.js.map +1 -6
- package/dist/globals.d.ts +4 -4
- package/dist/globals.js +9 -1
- package/dist/globals.js.map +1 -6
- package/dist/index.js +28 -27
- package/dist/index.js.map +1 -6
- package/dist/types/date-only.d.ts +64 -64
- package/dist/types/date-only.d.ts.map +1 -1
- package/dist/types/date-only.js +263 -252
- package/dist/types/date-only.js.map +1 -6
- package/dist/types/date-time.d.ts +36 -36
- package/dist/types/date-time.d.ts.map +1 -1
- package/dist/types/date-time.js +196 -288
- package/dist/types/date-time.js.map +1 -6
- package/dist/types/lazy-gc-map.d.ts +26 -26
- package/dist/types/lazy-gc-map.d.ts.map +1 -1
- package/dist/types/lazy-gc-map.js +202 -159
- package/dist/types/lazy-gc-map.js.map +1 -6
- package/dist/types/time.d.ts +23 -23
- package/dist/types/time.d.ts.map +1 -1
- package/dist/types/time.js +169 -158
- package/dist/types/time.js.map +1 -6
- package/dist/types/uuid.d.ts +11 -11
- package/dist/types/uuid.d.ts.map +1 -1
- package/dist/types/uuid.js +95 -70
- package/dist/types/uuid.js.map +1 -6
- package/dist/utils/bytes.d.ts +17 -17
- package/dist/utils/bytes.js +137 -81
- package/dist/utils/bytes.js.map +1 -6
- package/dist/utils/date-format.d.ts +40 -40
- package/dist/utils/date-format.js +187 -101
- package/dist/utils/date-format.js.map +1 -6
- package/dist/utils/error.d.ts +4 -4
- package/dist/utils/error.js +11 -6
- package/dist/utils/error.js.map +1 -6
- package/dist/utils/json.d.ts +19 -19
- package/dist/utils/json.js +187 -135
- package/dist/utils/json.js.map +1 -6
- package/dist/utils/num.d.ts +20 -20
- package/dist/utils/num.js +76 -34
- package/dist/utils/num.js.map +1 -6
- package/dist/utils/obj.d.ts +111 -111
- package/dist/utils/obj.d.ts.map +1 -1
- package/dist/utils/obj.js +706 -496
- package/dist/utils/obj.js.map +1 -6
- package/dist/utils/path.d.ts +10 -10
- package/dist/utils/path.js +35 -18
- package/dist/utils/path.js.map +1 -6
- package/dist/utils/primitive.d.ts +5 -5
- package/dist/utils/primitive.js +34 -14
- package/dist/utils/primitive.js.map +1 -6
- package/dist/utils/str.d.ts +38 -38
- package/dist/utils/str.js +217 -113
- package/dist/utils/str.js.map +1 -6
- package/dist/utils/template-strings.d.ts +26 -26
- package/dist/utils/template-strings.js +113 -40
- package/dist/utils/template-strings.js.map +1 -6
- package/dist/utils/transferable.d.ts +18 -18
- package/dist/utils/transferable.js +218 -151
- package/dist/utils/transferable.js.map +1 -6
- package/dist/utils/wait.d.ts +9 -9
- package/dist/utils/wait.js +30 -15
- package/dist/utils/wait.js.map +1 -6
- package/dist/utils/xml.d.ts +13 -13
- package/dist/utils/xml.js +84 -46
- package/dist/utils/xml.js.map +1 -6
- package/dist/utils/zip.d.ts +22 -22
- package/dist/utils/zip.js +172 -148
- package/dist/utils/zip.js.map +1 -6
- package/package.json +5 -7
- package/src/common.types.ts +14 -14
- package/src/env.ts +9 -1
- package/src/errors/argument-error.ts +15 -15
- package/src/errors/not-implemented-error.ts +9 -9
- package/src/errors/sd-error.ts +12 -12
- package/src/errors/timeout-error.ts +12 -12
- package/src/extensions/arr-ext.helpers.ts +16 -16
- package/src/extensions/arr-ext.ts +35 -35
- package/src/extensions/arr-ext.types.ts +57 -57
- package/src/extensions/map-ext.ts +16 -16
- package/src/extensions/set-ext.ts +11 -11
- package/src/features/debounce-queue.ts +23 -23
- package/src/features/event-emitter.ts +25 -25
- package/src/features/serial-queue.ts +13 -13
- package/src/globals.ts +4 -4
- package/src/index.ts +5 -5
- package/src/types/date-only.ts +84 -83
- package/src/types/date-time.ts +43 -42
- package/src/types/lazy-gc-map.ts +44 -44
- package/src/types/time.ts +29 -29
- package/src/types/uuid.ts +15 -15
- package/src/utils/bytes.ts +35 -35
- package/src/utils/date-format.ts +59 -59
- package/src/utils/error.ts +4 -4
- package/src/utils/json.ts +41 -41
- package/src/utils/num.ts +20 -20
- package/src/utils/obj.ts +138 -138
- package/src/utils/path.ts +10 -10
- package/src/utils/primitive.ts +6 -6
- package/src/utils/str.ts +48 -48
- package/src/utils/template-strings.ts +29 -29
- package/src/utils/transferable.ts +38 -38
- package/src/utils/wait.ts +10 -10
- package/src/utils/xml.ts +19 -19
- package/src/utils/zip.ts +25 -25
- package/README.md +0 -160
- package/docs/errors.md +0 -119
- package/docs/extensions.md +0 -387
- package/docs/features.md +0 -143
- package/docs/types.md +0 -287
- package/docs/utils.md +0 -757
- package/tests/errors/errors.spec.ts +0 -80
- package/tests/extensions/array-extension.spec.ts +0 -654
- package/tests/extensions/map-extension.spec.ts +0 -117
- package/tests/extensions/set-extension.spec.ts +0 -67
- package/tests/types/date-only.spec.ts +0 -533
- package/tests/types/date-time.spec.ts +0 -246
- package/tests/types/lazy-gc-map.spec.ts +0 -606
- package/tests/types/time.spec.ts +0 -428
- package/tests/types/uuid.spec.ts +0 -74
- package/tests/utils/bytes-utils.spec.ts +0 -197
- package/tests/utils/date-format.spec.ts +0 -350
- package/tests/utils/debounce-queue.spec.ts +0 -226
- package/tests/utils/json.spec.ts +0 -400
- package/tests/utils/number.spec.ts +0 -136
- package/tests/utils/object.spec.ts +0 -810
- package/tests/utils/path.spec.ts +0 -70
- package/tests/utils/primitive.spec.ts +0 -43
- package/tests/utils/sd-event-emitter.spec.ts +0 -189
- package/tests/utils/serial-queue.spec.ts +0 -305
- package/tests/utils/string.spec.ts +0 -265
- package/tests/utils/template-strings.spec.ts +0 -48
- package/tests/utils/transferable.spec.ts +0 -639
- package/tests/utils/wait.spec.ts +0 -123
- package/tests/utils/xml.spec.ts +0 -146
- package/tests/utils/zip.spec.ts +0 -221
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Array
|
|
2
|
+
* Array 확장 메서드
|
|
3
3
|
*
|
|
4
|
-
* @remarks
|
|
4
|
+
* @remarks 각 메서드의 TSDoc은 타입 정의 파일(arr-ext.types.ts)을 참조
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import "./map-ext";
|
|
@@ -28,7 +28,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
28
28
|
single<T>(predicate?: (item: T, index: number) => boolean): T | undefined {
|
|
29
29
|
const arr = predicate !== undefined ? this.filter(predicate) : this;
|
|
30
30
|
if (arr.length > 1) {
|
|
31
|
-
throw new ArgumentError("
|
|
31
|
+
throw new ArgumentError("여러 개의 결과가 발견되었습니다.", { count: arr.length });
|
|
32
32
|
}
|
|
33
33
|
return arr[0];
|
|
34
34
|
},
|
|
@@ -66,7 +66,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
66
66
|
},
|
|
67
67
|
|
|
68
68
|
ofType<T, N extends T>(type: PrimitiveTypeStr | Type<N>): N[] {
|
|
69
|
-
// PrimitiveTypeStr
|
|
69
|
+
// PrimitiveTypeStr 경우
|
|
70
70
|
if (typeof type === "string") {
|
|
71
71
|
return this.filter((item) => {
|
|
72
72
|
switch (type) {
|
|
@@ -87,15 +87,15 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
87
87
|
case "Bytes":
|
|
88
88
|
return item instanceof Uint8Array;
|
|
89
89
|
default: {
|
|
90
|
-
//
|
|
90
|
+
// 완전성 검사: PrimitiveTypeStr에 새 타입이 추가되면 컴파일 에러 발생
|
|
91
91
|
const _exhaustive: never = type;
|
|
92
|
-
throw new ArgumentError(
|
|
92
|
+
throw new ArgumentError(`지원하지 않는 타입: ${_exhaustive}`);
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
}) as N[];
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
// Type<N> (
|
|
98
|
+
// Type<N> (생성자) 경우
|
|
99
99
|
return this.filter((item) => item instanceof type || item?.constructor === type) as N[];
|
|
100
100
|
},
|
|
101
101
|
|
|
@@ -121,10 +121,10 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
121
121
|
return Promise.all(this.map(fn));
|
|
122
122
|
},
|
|
123
123
|
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
// -
|
|
127
|
-
// -
|
|
124
|
+
// key 기준으로 array 그룹화
|
|
125
|
+
// 성능 고려사항:
|
|
126
|
+
// - 원시 key (string, number 등): O(n) - Map 기반
|
|
127
|
+
// - 객체 key: O(n²) - objEqual 비교
|
|
128
128
|
groupBy<T, K, V>(
|
|
129
129
|
keySelector: (item: T, index: number) => K,
|
|
130
130
|
valueSelector?: (item: T, index: number) => V,
|
|
@@ -134,14 +134,14 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
134
134
|
}[] {
|
|
135
135
|
const result: { key: K; values: (V | T)[] }[] = [];
|
|
136
136
|
|
|
137
|
-
//
|
|
137
|
+
// 원시 key 최적화를 위한 Map (key 문자열 -> result index)
|
|
138
138
|
const primitiveKeyIndex = new Map<string, number>();
|
|
139
139
|
|
|
140
140
|
for (let i = 0; i < this.length; i++) {
|
|
141
141
|
const keyObj = keySelector(this[i], i);
|
|
142
142
|
const valueObj = valueSelector !== undefined ? valueSelector(this[i], i) : this[i];
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// 원시 key는 Map을 사용하여 O(n)으로 처리
|
|
145
145
|
if (keyObj == null || typeof keyObj !== "object") {
|
|
146
146
|
const keyStr = typeof keyObj + ":" + String(keyObj);
|
|
147
147
|
const existingIndex = primitiveKeyIndex.get(keyStr);
|
|
@@ -154,7 +154,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
154
154
|
continue;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
//
|
|
157
|
+
// 객체 key는 기존 방식 O(n²) 사용
|
|
158
158
|
const existsRecord = result.find((item) => equal(item.key, keyObj));
|
|
159
159
|
if (existsRecord !== undefined) {
|
|
160
160
|
existsRecord.values.push(valueObj);
|
|
@@ -179,7 +179,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
179
179
|
const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
|
|
180
180
|
|
|
181
181
|
if (result.has(keyObj)) {
|
|
182
|
-
throw new ArgumentError("
|
|
182
|
+
throw new ArgumentError("중복된 key입니다.", { duplicatedKey: keyObj });
|
|
183
183
|
}
|
|
184
184
|
result.set(keyObj, valueObj);
|
|
185
185
|
}
|
|
@@ -200,7 +200,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
200
200
|
const valueObj = valueSelector !== undefined ? await valueSelector(item, i) : item;
|
|
201
201
|
|
|
202
202
|
if (result.has(keyObj)) {
|
|
203
|
-
throw new ArgumentError("
|
|
203
|
+
throw new ArgumentError("중복된 key입니다.", { duplicatedKey: keyObj });
|
|
204
204
|
}
|
|
205
205
|
result.set(keyObj, valueObj);
|
|
206
206
|
}
|
|
@@ -282,9 +282,9 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
282
282
|
const key = keySelector(item, i);
|
|
283
283
|
const valueObj = valueSelector !== undefined ? valueSelector(item, i) : item;
|
|
284
284
|
|
|
285
|
-
// undefined
|
|
285
|
+
// undefined 값은 "없음"으로 처리하여 덮어쓰기 허용
|
|
286
286
|
if (result[key] !== undefined) {
|
|
287
|
-
throw new ArgumentError("
|
|
287
|
+
throw new ArgumentError("중복된 key입니다.", { duplicatedKey: key });
|
|
288
288
|
}
|
|
289
289
|
result[key] = valueObj;
|
|
290
290
|
}
|
|
@@ -293,7 +293,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
293
293
|
},
|
|
294
294
|
|
|
295
295
|
toTree<T, K extends keyof T, P extends keyof T>(key: K, parentKey: P): TreeArray<T>[] {
|
|
296
|
-
// O(n)
|
|
296
|
+
// O(n) 최적화: Map 기반 인덱싱
|
|
297
297
|
const childrenMap = this.toArrayMap((item) => item[parentKey]);
|
|
298
298
|
|
|
299
299
|
const fn = (items: T[]): TreeArray<T>[] => {
|
|
@@ -348,8 +348,8 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
348
348
|
const hasKeys = options?.keys !== undefined && options.keys.length > 0;
|
|
349
349
|
const excludeOpts = { topLevelExcludes: options?.excludes };
|
|
350
350
|
|
|
351
|
-
//
|
|
352
|
-
//
|
|
351
|
+
// keys 옵션이 제공되면 target을 Map으로 사전 인덱싱하여 O(n×m) → O(n+m)으로 개선
|
|
352
|
+
// 같은 key 값을 가진 target이 여러 개 존재할 수 있으므로 array로 저장
|
|
353
353
|
const keyIndexedTarget = hasKeys ? new Map<string, P[]>() : undefined;
|
|
354
354
|
|
|
355
355
|
if (keyIndexedTarget) {
|
|
@@ -367,11 +367,11 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
367
367
|
}
|
|
368
368
|
|
|
369
369
|
for (const sourceItem of this) {
|
|
370
|
-
//
|
|
370
|
+
// 전체 일치(sameTarget)를 우선하고, 없으면 key 일치(sameKeyTarget)를 검색
|
|
371
371
|
let sameTarget: P | undefined;
|
|
372
372
|
let sameKeyTarget: P | undefined;
|
|
373
373
|
|
|
374
|
-
//
|
|
374
|
+
// Set 기반 건너뛰기로 이미 매칭된 항목 스킵 (O(n) splice 제거 방지)
|
|
375
375
|
for (const targetItem of uncheckedTarget) {
|
|
376
376
|
if (!uncheckedTargetSet.has(targetItem)) continue;
|
|
377
377
|
if (equal(targetItem, sourceItem, excludeOpts)) {
|
|
@@ -380,14 +380,14 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
380
380
|
}
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
//
|
|
383
|
+
// 전체 일치가 없고 keys 옵션이 있으면 Map에서 O(1) 조회 수행
|
|
384
384
|
if (sameTarget === undefined && keyIndexedTarget) {
|
|
385
385
|
const sourceKeyStr = JSON.stringify(
|
|
386
386
|
options!.keys!.map((k) => (sourceItem as Record<string, unknown>)[k]),
|
|
387
387
|
);
|
|
388
388
|
const candidates = keyIndexedTarget.get(sourceKeyStr);
|
|
389
389
|
if (candidates && candidates.length > 0) {
|
|
390
|
-
//
|
|
390
|
+
// uncheckedTargetSet에서 O(1) 조회로 남은 첫 번째 항목 선택
|
|
391
391
|
sameKeyTarget = candidates.find((c) => uncheckedTargetSet.has(c));
|
|
392
392
|
}
|
|
393
393
|
}
|
|
@@ -473,22 +473,22 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
473
473
|
|
|
474
474
|
const result: (T | P | (T & P))[] = clone(this);
|
|
475
475
|
|
|
476
|
-
//
|
|
476
|
+
// source 항목의 원래 index를 사전 계산하여 O(n) 조회를 O(1)로 개선
|
|
477
477
|
const sourceIndexMap = new Map<T, number>();
|
|
478
478
|
for (let i = 0; i < this.length; i++) {
|
|
479
479
|
sourceIndexMap.set(this[i], i);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
for (const diff of diffs) {
|
|
483
|
-
//
|
|
483
|
+
// 업데이트 시
|
|
484
484
|
if (diff.source !== undefined && diff.target !== undefined) {
|
|
485
485
|
const sourceIndex = sourceIndexMap.get(diff.source);
|
|
486
486
|
if (sourceIndex === undefined) {
|
|
487
|
-
throw new SdError("
|
|
487
|
+
throw new SdError("예상치 못한 오류: merge에서 source 항목을 찾을 수 없습니다.");
|
|
488
488
|
}
|
|
489
489
|
result[sourceIndex] = merge(diff.source, diff.target);
|
|
490
490
|
}
|
|
491
|
-
//
|
|
491
|
+
// 추가 시
|
|
492
492
|
else if (diff.target !== undefined) {
|
|
493
493
|
result.push(diff.target);
|
|
494
494
|
}
|
|
@@ -502,7 +502,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
502
502
|
for (let i = 0; i < this.length; i++) {
|
|
503
503
|
const item = selector !== undefined ? selector(this[i], i) : this[i];
|
|
504
504
|
if (typeof item !== "number") {
|
|
505
|
-
throw new ArgumentError("sum
|
|
505
|
+
throw new ArgumentError("sum은 숫자에만 사용할 수 있습니다.", {
|
|
506
506
|
type: typeof item,
|
|
507
507
|
});
|
|
508
508
|
}
|
|
@@ -517,7 +517,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
517
517
|
for (let i = 0; i < this.length; i++) {
|
|
518
518
|
const item = selector !== undefined ? selector(this[i], i) : this[i];
|
|
519
519
|
if (typeof item !== "number" && typeof item !== "string") {
|
|
520
|
-
throw new ArgumentError("min
|
|
520
|
+
throw new ArgumentError("min은 숫자/문자열에만 사용할 수 있습니다.", {
|
|
521
521
|
type: typeof item,
|
|
522
522
|
});
|
|
523
523
|
}
|
|
@@ -534,7 +534,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
|
|
|
534
534
|
for (let i = 0; i < this.length; i++) {
|
|
535
535
|
const item = selector !== undefined ? selector(this[i], i) : this[i];
|
|
536
536
|
if (typeof item !== "number" && typeof item !== "string") {
|
|
537
|
-
throw new ArgumentError("max
|
|
537
|
+
throw new ArgumentError("max는 숫자/문자열에만 사용할 수 있습니다.", {
|
|
538
538
|
type: typeof item,
|
|
539
539
|
});
|
|
540
540
|
}
|
|
@@ -571,7 +571,7 @@ const arrayMutableExtensions: MutableArrayExt<any> & ThisType<any[]> = {
|
|
|
571
571
|
indicesToRemove.push(i);
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
|
-
//
|
|
574
|
+
// index 변동을 방지하기 위해 역순으로 제거
|
|
575
575
|
for (let i = indicesToRemove.length - 1; i >= 0; i--) {
|
|
576
576
|
this.splice(indicesToRemove[i], 1);
|
|
577
577
|
}
|
|
@@ -609,7 +609,7 @@ const arrayMutableExtensions: MutableArrayExt<any> & ThisType<any[]> = {
|
|
|
609
609
|
? (itemOrSelector as (item: T, index: number) => boolean)
|
|
610
610
|
: (item: T) => item === itemOrSelector;
|
|
611
611
|
|
|
612
|
-
//
|
|
612
|
+
// index 변경 문제를 방지하기 위한 역순 순회 (O(n) 성능)
|
|
613
613
|
for (let i = this.length - 1; i >= 0; i--) {
|
|
614
614
|
if (shouldRemove(this[i], i)) {
|
|
615
615
|
this.splice(i, 1);
|
|
@@ -647,7 +647,7 @@ for (const [name, fn] of Object.entries({
|
|
|
647
647
|
|
|
648
648
|
//#endregion
|
|
649
649
|
|
|
650
|
-
//#region
|
|
650
|
+
//#region 타입 선언
|
|
651
651
|
|
|
652
652
|
declare global {
|
|
653
653
|
interface ReadonlyArray<T> extends ReadonlyArrayExt<T> {}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Array
|
|
2
|
+
* Array 확장 타입 정의
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { PrimitiveTypeMap, PrimitiveTypeStr, Type } from "../common.types";
|
|
@@ -11,67 +11,67 @@ import type { Time } from "../types/time";
|
|
|
11
11
|
|
|
12
12
|
export interface ReadonlyArrayExt<TItem> {
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* @param predicate
|
|
16
|
-
* @returns
|
|
17
|
-
* @throws ArgumentError
|
|
14
|
+
* 조건에 맞는 단일 요소 반환
|
|
15
|
+
* @param predicate 필터 조건 (생략 시 전체 array 대상)
|
|
16
|
+
* @returns 요소가 없으면 undefined
|
|
17
|
+
* @throws ArgumentError 2개 이상의 요소가 조건에 맞을 경우
|
|
18
18
|
*/
|
|
19
19
|
single(predicate?: (item: TItem, index: number) => boolean): TItem | undefined;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param predicate
|
|
24
|
-
* @returns
|
|
22
|
+
* 첫 번째 요소 반환
|
|
23
|
+
* @param predicate 필터 조건 (생략 시 첫 번째 요소 반환)
|
|
24
|
+
* @returns 요소가 없으면 undefined
|
|
25
25
|
*/
|
|
26
26
|
first(predicate?: (item: TItem, index: number) => boolean): TItem | undefined;
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/** 비동기 필터 (순차 실행) */
|
|
29
29
|
filterAsync(predicate: (item: TItem, index: number) => Promise<boolean>): Promise<TItem[]>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
* @param predicate
|
|
34
|
-
* @returns
|
|
32
|
+
* 마지막 요소 반환
|
|
33
|
+
* @param predicate 필터 조건 (생략 시 마지막 요소 반환)
|
|
34
|
+
* @returns 요소가 없으면 undefined
|
|
35
35
|
*/
|
|
36
36
|
last(predicate?: (item: TItem, index: number) => boolean): TItem | undefined;
|
|
37
37
|
|
|
38
|
-
/**
|
|
38
|
+
/** null/undefined 제거 */
|
|
39
39
|
filterExists(): NonNullable<TItem>[];
|
|
40
40
|
|
|
41
|
-
/**
|
|
41
|
+
/** 특정 타입의 요소만 필터 (PrimitiveTypeStr 또는 생성자 타입) */
|
|
42
42
|
ofType<TKey extends PrimitiveTypeStr>(type: TKey): Extract<TItem, PrimitiveTypeMap[TKey]>[];
|
|
43
43
|
ofType<TNarrow extends TItem>(type: Type<TNarrow>): TNarrow[];
|
|
44
44
|
|
|
45
|
-
/**
|
|
45
|
+
/** 비동기 매핑 (순차 실행) */
|
|
46
46
|
mapAsync<TResult>(selector: (item: TItem, index: number) => Promise<TResult>): Promise<TResult[]>;
|
|
47
47
|
|
|
48
|
-
/**
|
|
48
|
+
/** 중첩 array 평탄화 */
|
|
49
49
|
mapMany(): TItem extends readonly (infer U)[] ? U[] : TItem;
|
|
50
50
|
|
|
51
|
-
/**
|
|
51
|
+
/** 매핑 후 평탄화 */
|
|
52
52
|
mapMany<TResult>(selector: (item: TItem, index: number) => TResult[]): TResult[];
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/** 비동기 매핑 후 평탄화 (순차 실행) */
|
|
55
55
|
mapManyAsync<TResult>(selector: (item: TItem, index: number) => Promise<TResult[]>): Promise<TResult[]>;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
* @note
|
|
58
|
+
* 비동기 병렬 처리 (Promise.all 사용)
|
|
59
|
+
* @note 하나라도 reject되면 전체가 즉시 reject됨 (Promise.all 동작)
|
|
60
60
|
*/
|
|
61
61
|
parallelAsync<TResult>(fn: (item: TItem, index: number) => Promise<TResult>): Promise<TResult[]>;
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
65
|
-
* @param keySelector
|
|
66
|
-
* @note O(n²)
|
|
64
|
+
* key 기준 그룹화
|
|
65
|
+
* @param keySelector 그룹의 key 선택 함수
|
|
66
|
+
* @note O(n²) 복잡도 (객체 key 지원을 위한 깊은 비교). 원시 key만 필요하면 toArrayMap()이 O(n)으로 더 효율적
|
|
67
67
|
*/
|
|
68
68
|
groupBy<TKey>(keySelector: (item: TItem, index: number) => TKey): { key: TKey; values: TItem[] }[];
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
72
|
-
* @param keySelector
|
|
73
|
-
* @param valueSelector
|
|
74
|
-
* @note O(n²)
|
|
71
|
+
* key 기준 그룹화 (value 변환 포함)
|
|
72
|
+
* @param keySelector 그룹의 key 선택 함수
|
|
73
|
+
* @param valueSelector value 변환 함수
|
|
74
|
+
* @note O(n²) 복잡도 (객체 key 지원을 위한 깊은 비교). 원시 key만 필요하면 toArrayMap()이 O(n)으로 더 효율적
|
|
75
75
|
*/
|
|
76
76
|
groupBy<TKey, TValue>(
|
|
77
77
|
keySelector: (item: TItem, index: number) => TKey,
|
|
@@ -121,16 +121,16 @@ export interface ReadonlyArrayExt<TItem> {
|
|
|
121
121
|
): Record<string, TValue>;
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* 평면 array를 트리 구조로 변환
|
|
125
125
|
*
|
|
126
|
-
* @param keyProp
|
|
127
|
-
* @param parentKey
|
|
128
|
-
* @returns
|
|
126
|
+
* @param keyProp 각 항목의 고유 key 속성명
|
|
127
|
+
* @param parentKey 부모 항목의 key를 참조하는 속성명
|
|
128
|
+
* @returns 루트 항목 array (각 항목에 children 속성이 추가됨)
|
|
129
129
|
*
|
|
130
130
|
* @remarks
|
|
131
|
-
* -
|
|
132
|
-
* -
|
|
133
|
-
* -
|
|
131
|
+
* - parentKey 값이 null/undefined인 항목이 루트가 됨
|
|
132
|
+
* - 내부적으로 toArrayMap을 사용하여 O(n) 복잡도
|
|
133
|
+
* - 원본 항목은 복사되고 children 속성이 추가됨
|
|
134
134
|
*
|
|
135
135
|
* @example
|
|
136
136
|
* ```typescript
|
|
@@ -162,9 +162,9 @@ export interface ReadonlyArrayExt<TItem> {
|
|
|
162
162
|
): TreeArray<TItem>[];
|
|
163
163
|
|
|
164
164
|
/**
|
|
165
|
-
*
|
|
166
|
-
* @param options matchAddress:
|
|
167
|
-
* @note O(n²)
|
|
165
|
+
* 중복 제거
|
|
166
|
+
* @param options matchAddress: 주소 비교 (true면 Set 사용), keyFn: 커스텀 key 함수 (O(n) 성능)
|
|
167
|
+
* @note 객체 array에서 keyFn 없이 사용하면 O(n²) 복잡도. 대량 데이터에는 keyFn 사용 권장
|
|
168
168
|
*/
|
|
169
169
|
distinct(
|
|
170
170
|
options?: boolean | { matchAddress?: boolean; keyFn?: (item: TItem) => string | number },
|
|
@@ -179,10 +179,10 @@ export interface ReadonlyArrayExt<TItem> {
|
|
|
179
179
|
): TItem[];
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
|
-
*
|
|
183
|
-
* @param target
|
|
184
|
-
* @param options keys:
|
|
185
|
-
* @note
|
|
182
|
+
* 두 array 비교 (INSERT/DELETE/UPDATE)
|
|
183
|
+
* @param target 비교할 array
|
|
184
|
+
* @param options keys: key 비교용, excludes: 비교에서 제외할 속성
|
|
185
|
+
* @note target에 중복 key가 있으면 첫 번째 매칭만 사용됨
|
|
186
186
|
*/
|
|
187
187
|
diffs<TOtherItem>(
|
|
188
188
|
target: TOtherItem[],
|
|
@@ -233,9 +233,9 @@ export interface ReadonlyArrayExt<TItem> {
|
|
|
233
233
|
): (TItem | TOtherItem | (TItem & TOtherItem))[];
|
|
234
234
|
|
|
235
235
|
/**
|
|
236
|
-
*
|
|
237
|
-
* @param selector
|
|
238
|
-
* @returns
|
|
236
|
+
* 요소의 합계 반환
|
|
237
|
+
* @param selector value 선택 함수 (생략 시 요소 자체를 숫자로 사용)
|
|
238
|
+
* @returns array가 비어 있으면 0
|
|
239
239
|
*/
|
|
240
240
|
sum(selector?: (item: TItem, index: number) => number): number;
|
|
241
241
|
|
|
@@ -251,43 +251,43 @@ export interface ReadonlyArrayExt<TItem> {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/**
|
|
254
|
-
*
|
|
255
|
-
* @mutates
|
|
254
|
+
* 원본 array를 변경하는 확장 메서드
|
|
255
|
+
* @mutates 모든 메서드가 원본 array를 직접 수정함
|
|
256
256
|
*/
|
|
257
257
|
export interface MutableArrayExt<TItem> {
|
|
258
258
|
/**
|
|
259
|
-
*
|
|
260
|
-
* @param options matchAddress:
|
|
261
|
-
* @note O(n²)
|
|
259
|
+
* 원본 array에서 중복 제거
|
|
260
|
+
* @param options matchAddress: 주소 비교 (true면 Set 사용), keyFn: 커스텀 key 함수 (O(n) 성능)
|
|
261
|
+
* @note 객체 array에서 keyFn 없이 사용하면 O(n²) 복잡도. 대량 데이터에는 keyFn 사용 권장
|
|
262
262
|
* @mutates
|
|
263
263
|
*/
|
|
264
264
|
distinctThis(
|
|
265
265
|
options?: boolean | { matchAddress?: boolean; keyFn?: (item: TItem) => string | number },
|
|
266
266
|
): TItem[];
|
|
267
267
|
|
|
268
|
-
/**
|
|
268
|
+
/** 원본 array를 오름차순 정렬 @mutates */
|
|
269
269
|
orderByThis(
|
|
270
270
|
selector?: (item: TItem) => string | number | DateOnly | DateTime | Time | undefined,
|
|
271
271
|
): TItem[];
|
|
272
272
|
|
|
273
|
-
/**
|
|
273
|
+
/** 원본 array를 내림차순 정렬 @mutates */
|
|
274
274
|
orderByDescThis(
|
|
275
275
|
selector?: (item: TItem) => string | number | DateOnly | DateTime | Time | undefined,
|
|
276
276
|
): TItem[];
|
|
277
277
|
|
|
278
|
-
/**
|
|
278
|
+
/** 원본 array에 항목 삽입 @mutates */
|
|
279
279
|
insert(index: number, ...items: TItem[]): this;
|
|
280
280
|
|
|
281
|
-
/**
|
|
281
|
+
/** 원본 array에서 항목 제거 @mutates */
|
|
282
282
|
remove(item: TItem): this;
|
|
283
283
|
|
|
284
|
-
/**
|
|
284
|
+
/** 원본 array에서 조건에 맞는 항목 제거 @mutates */
|
|
285
285
|
remove(selector: (item: TItem, index: number) => boolean): this;
|
|
286
286
|
|
|
287
|
-
/**
|
|
287
|
+
/** 원본 array에서 항목 토글 (있으면 제거, 없으면 추가) @mutates */
|
|
288
288
|
toggle(item: TItem): this;
|
|
289
289
|
|
|
290
|
-
/**
|
|
290
|
+
/** 원본 array 비우기 @mutates */
|
|
291
291
|
clear(): this;
|
|
292
292
|
}
|
|
293
293
|
|
|
@@ -307,7 +307,7 @@ export type ArrayOneWayDiffResult<TItem> =
|
|
|
307
307
|
|
|
308
308
|
export type TreeArray<TNode> = TNode & { children: TreeArray<TNode>[] };
|
|
309
309
|
|
|
310
|
-
/**
|
|
310
|
+
/** 정렬/비교 가능한 타입 */
|
|
311
311
|
export type ComparableType = string | number | boolean | DateTime | DateOnly | Time | undefined;
|
|
312
312
|
|
|
313
313
|
//#endregion
|
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Map
|
|
2
|
+
* Map 확장 메서드
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
declare global {
|
|
6
6
|
interface Map<K, V> {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* key에 해당하는 값이 없으면 새 값을 설정하고 반환
|
|
9
9
|
*
|
|
10
10
|
* @remarks
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* **주의**: V 타입이 함수인 경우 (예: `Map<string, () => void>`),
|
|
12
|
+
* 함수를 두 번째 인자로 직접 전달하면 팩토리로 인식되어 호출됨.
|
|
13
|
+
* 함수 자체를 값으로 저장하려면 팩토리로 감싸야 함.
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```typescript
|
|
17
|
-
* //
|
|
17
|
+
* // 일반 값
|
|
18
18
|
* map.getOrCreate("key", 0);
|
|
19
19
|
* map.getOrCreate("key", []);
|
|
20
20
|
*
|
|
21
|
-
* //
|
|
21
|
+
* // 팩토리 함수 (비용이 큰 연산에 사용)
|
|
22
22
|
* map.getOrCreate("key", () => expensiveComputation());
|
|
23
23
|
*
|
|
24
|
-
* //
|
|
24
|
+
* // 함수를 값으로 저장
|
|
25
25
|
* const fnMap = new Map<string, () => void>();
|
|
26
26
|
* const myFn = () => console.log("hello");
|
|
27
|
-
* fnMap.getOrCreate("key", () => myFn); //
|
|
27
|
+
* fnMap.getOrCreate("key", () => myFn); // 팩토리로 감싸기
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
30
|
getOrCreate(key: K, newValue: V): V;
|
|
31
31
|
getOrCreate(key: K, newValueFn: () => V): V;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* 함수를 사용하여 key의 값을 업데이트
|
|
35
35
|
*
|
|
36
|
-
* @param key
|
|
37
|
-
* @param updateFn
|
|
36
|
+
* @param key 업데이트할 key
|
|
37
|
+
* @param updateFn 현재 값을 받아 새 값을 반환하는 함수 (key가 없으면 undefined)
|
|
38
38
|
*
|
|
39
39
|
* @remarks
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* key가 존재하지 않아도 updateFn이 호출되어 새 값이 설정됨.
|
|
41
|
+
* 기존 값 기반 계산(카운터 증가, array에 추가 등)에 유용함.
|
|
42
42
|
*
|
|
43
43
|
* @example
|
|
44
44
|
* ```typescript
|
|
45
45
|
* const countMap = new Map<string, number>();
|
|
46
46
|
*
|
|
47
|
-
* //
|
|
47
|
+
* // 카운터 증가
|
|
48
48
|
* countMap.update("key", (v) => (v ?? 0) + 1);
|
|
49
49
|
*
|
|
50
|
-
* //
|
|
50
|
+
* // array에 항목 추가
|
|
51
51
|
* const arrayMap = new Map<string, string[]>();
|
|
52
52
|
* arrayMap.update("key", (v) => [...(v ?? []), "item"]);
|
|
53
53
|
* ```
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Set
|
|
2
|
+
* Set 확장 메서드
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
declare global {
|
|
6
6
|
interface Set<T> {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* 여러 값을 한 번에 추가
|
|
9
9
|
*/
|
|
10
10
|
adds(...values: T[]): this;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* 값 토글 (있으면 제거, 없으면 추가)
|
|
14
14
|
*
|
|
15
|
-
* @param value
|
|
16
|
-
* @param addOrDel
|
|
17
|
-
* @returns this (
|
|
15
|
+
* @param value 토글할 값
|
|
16
|
+
* @param addOrDel 강제 추가("add") 또는 제거("del") (생략 시 자동 토글)
|
|
17
|
+
* @returns this (메서드 체이닝 가능)
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
20
|
-
* addOrDel
|
|
20
|
+
* addOrDel 매개변수로 조건부 추가/제거를 간결하게 표현할 수 있음.
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* ```typescript
|
|
24
24
|
* const set = new Set<number>([1, 2, 3]);
|
|
25
25
|
*
|
|
26
|
-
* set.toggle(2); // 2
|
|
27
|
-
* set.toggle(4); // 4
|
|
26
|
+
* set.toggle(2); // 2가 있으므로 제거 → {1, 3}
|
|
27
|
+
* set.toggle(4); // 4가 없으므로 추가 → {1, 3, 4}
|
|
28
28
|
*
|
|
29
|
-
* //
|
|
29
|
+
* // 조건부 토글
|
|
30
30
|
* const isAdmin = true;
|
|
31
|
-
* set.toggle(5, isAdmin ? "add" : "del"); //
|
|
31
|
+
* set.toggle(5, isAdmin ? "add" : "del"); // 강제 추가
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
toggle(value: T, addOrDel?: "add" | "del"): this;
|