@web-ts-toolkit/utils 0.28.0 → 0.29.0

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/README.md CHANGED
@@ -19,7 +19,7 @@ pnpm add @web-ts-toolkit/utils
19
19
  ## Quick Start
20
20
 
21
21
  ```ts
22
- import { get, set, normalizeUrlPath, parseBooleanString } from '@web-ts-toolkit/utils';
22
+ import { get, groupBy, normalizeUrlPath, parseBooleanString, startCase, sumBy } from '@web-ts-toolkit/utils';
23
23
 
24
24
  const payload = {
25
25
  user: {
@@ -30,15 +30,25 @@ const payload = {
30
30
  };
31
31
 
32
32
  get(payload, 'user.profile.name');
33
- set(payload, 'user.profile.role', 'admin');
33
+ groupBy(
34
+ [
35
+ { type: 'fruit', name: 'apple' },
36
+ { type: 'fruit', name: 'banana' },
37
+ { type: 'vegetable', name: 'carrot' },
38
+ ],
39
+ 'type',
40
+ );
41
+ startCase('api_response_time');
42
+ sumBy([{ hours: 2 }, { hours: 3 }], 'hours');
34
43
  normalizeUrlPath('api//users');
35
44
  parseBooleanString('true', false);
36
45
  ```
37
46
 
38
47
  ## Main Exports
39
48
 
40
- - object helpers: `get`, `set`, `pick`, `omit`, `assign`, `cloneDeep`
41
- - collection helpers: `map`, `filter`, `reduce`, `find`, `flatten`, `uniq`, `orderBy`
49
+ - object helpers: `get`, `set`, `pick`, `pickBy`, `omit`, `assign`, `cloneDeep`, `mapKeys`
50
+ - collection helpers: `map`, `filter`, `reduce`, `find`, `flatten`, `uniq`, `orderBy`, `groupBy`, `sum`, `sumBy`
51
+ - string helpers: `startCase`, `upperCase`
42
52
  - guards: `isArray`, `isPlainObject`, `isString`, `isPromise`
43
53
  - URL helpers: `addLeadingSlash`, `removeConsecutiveSlashesFromUrl`, `normalizeUrlPath`
44
54
 
package/index.d.mts CHANGED
@@ -27,6 +27,8 @@ type PropertyPath = string | number | Array<string | number>;
27
27
 
28
28
  declare function get<T = unknown>(object: unknown, path: PropertyPath, defaultValue?: T): T;
29
29
 
30
+ declare function groupBy<T>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => unknown)): Record<string, T[]>;
31
+
30
32
  declare function intersection<T>(...arrays: Array<T[] | null | undefined>): T[];
31
33
 
32
34
  declare function intersectionBy<T>(...args: unknown[]): T[];
@@ -63,6 +65,8 @@ declare function keys(value: unknown): string[];
63
65
 
64
66
  declare function map<T, TResult>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => TResult)): TResult[];
65
67
 
68
+ declare function mapKeys<TValue>(object: Record<string, TValue> | null | undefined, iteratee: (value: TValue, key: string, object: Record<string, TValue>) => string): Record<string, TValue>;
69
+
66
70
  declare function mapValues<TValue, TResult>(object: Record<string, TValue> | null | undefined, iteratee: (value: TValue, key: string, object: Record<string, TValue>) => TResult): Record<string, TResult>;
67
71
 
68
72
  declare function mapValuesAsync<TObject extends Record<string, unknown>, TResult>(object: TObject, asyncFn: (value: TObject[keyof TObject], key: string, object: TObject) => Promise<TResult> | TResult): Promise<Record<string, TResult>>;
@@ -79,6 +83,8 @@ declare function padEnd(value: unknown, length?: number, chars?: string): string
79
83
 
80
84
  declare function pick<T extends object>(object: T, paths: PropertyPath | PropertyPath[]): Partial<T>;
81
85
 
86
+ declare function pickBy<TValue>(object: Record<string, TValue> | null | undefined, predicate: (value: TValue, key: string, object: Record<string, TValue>) => boolean): Record<string, TValue>;
87
+
82
88
  declare function normalizeUrlPath(url: string): string;
83
89
 
84
90
  declare function reduce<T, TResult>(collection: T[] | null | undefined, iteratee: (accumulator: TResult, value: T, key: number, collection: T[]) => TResult, accumulator?: TResult): TResult;
@@ -88,10 +94,18 @@ declare function removeConsecutiveSlashesFromUrl(url: string): string;
88
94
 
89
95
  declare function set<T>(object: T, path: PropertyPath, value: unknown): T;
90
96
 
97
+ declare function startCase(value: unknown): string;
98
+
99
+ declare function sum(values: number[] | null | undefined): number;
100
+
101
+ declare function sumBy<T>(collection: T[] | null | undefined, iteratee: string | number | ((value: T, key: number, collection: T[]) => number | undefined)): number;
102
+
91
103
  declare function toAsyncFn<TArgs extends unknown[], TResult>(fn?: ((this: unknown, ...args: TArgs) => TResult | PromiseLike<TResult>) | null, defaultValue?: TResult): (this: unknown, ...args: TArgs) => PromiseLike<unknown> | Promise<any>;
92
104
 
93
105
  declare function toStringRecord(value: unknown): Record<string, string> | undefined;
94
106
 
95
107
  declare function uniq<T>(array: T[] | null | undefined): T[];
96
108
 
97
- export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, reduce, removeConsecutiveSlashesFromUrl, set, toAsyncFn, toStringRecord, uniq };
109
+ declare function upperCase(value: unknown): string;
110
+
111
+ export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, groupBy, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, upperCase };
package/index.d.ts CHANGED
@@ -27,6 +27,8 @@ type PropertyPath = string | number | Array<string | number>;
27
27
 
28
28
  declare function get<T = unknown>(object: unknown, path: PropertyPath, defaultValue?: T): T;
29
29
 
30
+ declare function groupBy<T>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => unknown)): Record<string, T[]>;
31
+
30
32
  declare function intersection<T>(...arrays: Array<T[] | null | undefined>): T[];
31
33
 
32
34
  declare function intersectionBy<T>(...args: unknown[]): T[];
@@ -63,6 +65,8 @@ declare function keys(value: unknown): string[];
63
65
 
64
66
  declare function map<T, TResult>(collection: T[] | Record<string, T> | null | undefined, iteratee: string | number | ((value: T, key: number | string, collection: T[] | Record<string, T>) => TResult)): TResult[];
65
67
 
68
+ declare function mapKeys<TValue>(object: Record<string, TValue> | null | undefined, iteratee: (value: TValue, key: string, object: Record<string, TValue>) => string): Record<string, TValue>;
69
+
66
70
  declare function mapValues<TValue, TResult>(object: Record<string, TValue> | null | undefined, iteratee: (value: TValue, key: string, object: Record<string, TValue>) => TResult): Record<string, TResult>;
67
71
 
68
72
  declare function mapValuesAsync<TObject extends Record<string, unknown>, TResult>(object: TObject, asyncFn: (value: TObject[keyof TObject], key: string, object: TObject) => Promise<TResult> | TResult): Promise<Record<string, TResult>>;
@@ -79,6 +83,8 @@ declare function padEnd(value: unknown, length?: number, chars?: string): string
79
83
 
80
84
  declare function pick<T extends object>(object: T, paths: PropertyPath | PropertyPath[]): Partial<T>;
81
85
 
86
+ declare function pickBy<TValue>(object: Record<string, TValue> | null | undefined, predicate: (value: TValue, key: string, object: Record<string, TValue>) => boolean): Record<string, TValue>;
87
+
82
88
  declare function normalizeUrlPath(url: string): string;
83
89
 
84
90
  declare function reduce<T, TResult>(collection: T[] | null | undefined, iteratee: (accumulator: TResult, value: T, key: number, collection: T[]) => TResult, accumulator?: TResult): TResult;
@@ -88,10 +94,18 @@ declare function removeConsecutiveSlashesFromUrl(url: string): string;
88
94
 
89
95
  declare function set<T>(object: T, path: PropertyPath, value: unknown): T;
90
96
 
97
+ declare function startCase(value: unknown): string;
98
+
99
+ declare function sum(values: number[] | null | undefined): number;
100
+
101
+ declare function sumBy<T>(collection: T[] | null | undefined, iteratee: string | number | ((value: T, key: number, collection: T[]) => number | undefined)): number;
102
+
91
103
  declare function toAsyncFn<TArgs extends unknown[], TResult>(fn?: ((this: unknown, ...args: TArgs) => TResult | PromiseLike<TResult>) | null, defaultValue?: TResult): (this: unknown, ...args: TArgs) => PromiseLike<unknown> | Promise<any>;
92
104
 
93
105
  declare function toStringRecord(value: unknown): Record<string, string> | undefined;
94
106
 
95
107
  declare function uniq<T>(array: T[] | null | undefined): T[];
96
108
 
97
- export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, reduce, removeConsecutiveSlashesFromUrl, set, toAsyncFn, toStringRecord, uniq };
109
+ declare function upperCase(value: unknown): string;
110
+
111
+ export { addLeadingSlash, arrayToRecord, assign, castArray, cloneDeep, compact, difference, filter, find, flatten, flattenDeep, forEach, get, groupBy, intersection, intersectionBy, isArray, isBoolean, isEmpty, isEqual, isFunction, isMatch, isNaNValue as isNaN, isNil, isNumber, isObject, isPlainObject, isPromise, isString, isUndefined, keys, map, mapKeys, mapValues, mapValuesAsync, noop, normalizeUrlPath, omit, orderBy, padEnd, parseBooleanString, pick, pickBy, reduce, removeConsecutiveSlashesFromUrl, set, startCase, sum, sumBy, toAsyncFn, toStringRecord, uniq, upperCase };
package/index.js CHANGED
@@ -32,6 +32,7 @@ __export(index_exports, {
32
32
  flattenDeep: () => flattenDeep,
33
33
  forEach: () => forEach,
34
34
  get: () => get,
35
+ groupBy: () => groupBy,
35
36
  intersection: () => intersection,
36
37
  intersectionBy: () => intersectionBy,
37
38
  isArray: () => isArray,
@@ -50,6 +51,7 @@ __export(index_exports, {
50
51
  isUndefined: () => isUndefined,
51
52
  keys: () => keys,
52
53
  map: () => map,
54
+ mapKeys: () => mapKeys,
53
55
  mapValues: () => mapValues,
54
56
  mapValuesAsync: () => mapValuesAsync,
55
57
  noop: () => noop,
@@ -59,12 +61,17 @@ __export(index_exports, {
59
61
  padEnd: () => padEnd,
60
62
  parseBooleanString: () => parseBooleanString,
61
63
  pick: () => pick,
64
+ pickBy: () => pickBy,
62
65
  reduce: () => reduce,
63
66
  removeConsecutiveSlashesFromUrl: () => removeConsecutiveSlashesFromUrl,
64
67
  set: () => set,
68
+ startCase: () => startCase,
69
+ sum: () => sum,
70
+ sumBy: () => sumBy,
65
71
  toAsyncFn: () => toAsyncFn,
66
72
  toStringRecord: () => toStringRecord,
67
- uniq: () => uniq
73
+ uniq: () => uniq,
74
+ upperCase: () => upperCase
68
75
  });
69
76
  module.exports = __toCommonJS(index_exports);
70
77
 
@@ -94,6 +101,7 @@ function castArray(value) {
94
101
 
95
102
  // src/_internal.ts
96
103
  var pathPattern = /[^.[\]]+|\[(?:([^"'[\]]+)|["']([^"']+)["'])\]/g;
104
+ var unsafePathParts = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
97
105
  function isObject(value) {
98
106
  return value !== null && (typeof value === "object" || typeof value === "function");
99
107
  }
@@ -126,6 +134,15 @@ function toPath(path) {
126
134
  });
127
135
  return result.length > 0 ? result : [input];
128
136
  }
137
+ function hasUnsafePathPart(parts) {
138
+ for (let index = 0; index < parts.length; index++) {
139
+ const part = parts[index];
140
+ if (typeof part === "string" && unsafePathParts.has(part)) {
141
+ return true;
142
+ }
143
+ }
144
+ return false;
145
+ }
129
146
  function hasPath(value, path) {
130
147
  const parts = toPath(path);
131
148
  if (parts.length === 0) {
@@ -160,7 +177,7 @@ function setPath(target, path, value) {
160
177
  return target;
161
178
  }
162
179
  const parts = toPath(path);
163
- if (parts.length === 0) {
180
+ if (parts.length === 0 || hasUnsafePathPart(parts)) {
164
181
  return target;
165
182
  }
166
183
  let current = target;
@@ -181,7 +198,7 @@ function deletePath(target, path) {
181
198
  return;
182
199
  }
183
200
  const parts = toPath(path);
184
- if (parts.length === 0) {
201
+ if (parts.length === 0 || hasUnsafePathPart(parts)) {
185
202
  return;
186
203
  }
187
204
  let current = target;
@@ -227,6 +244,14 @@ function getIteratee(iteratee) {
227
244
  }
228
245
  return (value) => getPath(value, iteratee);
229
246
  }
247
+ var wordPattern = /[A-Z]{2,}(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]+|[0-9]+/g;
248
+ function words(value) {
249
+ const input = String(value ?? "");
250
+ if (input.length === 0) {
251
+ return [];
252
+ }
253
+ return input.replace(/[']/g, "").match(wordPattern) ?? [];
254
+ }
230
255
  function compareAscending(left, right) {
231
256
  if (sameValueZero(left, right)) {
232
257
  return 0;
@@ -487,6 +512,31 @@ function get(object, path, defaultValue) {
487
512
  return getPath(object, path, defaultValue);
488
513
  }
489
514
 
515
+ // src/groupBy.ts
516
+ function groupBy(collection, iteratee) {
517
+ if (!collection) {
518
+ return {};
519
+ }
520
+ const callback = getIteratee(iteratee);
521
+ const result = {};
522
+ if (Array.isArray(collection)) {
523
+ for (let index = 0; index < collection.length; index++) {
524
+ const value = collection[index];
525
+ const key = String(callback(value, index, collection));
526
+ (result[key] ??= []).push(value);
527
+ }
528
+ return result;
529
+ }
530
+ const keys2 = Object.keys(collection);
531
+ for (let index = 0; index < keys2.length; index++) {
532
+ const key = keys2[index];
533
+ const value = collection[key];
534
+ const group = String(callback(value, key, collection));
535
+ (result[group] ??= []).push(value);
536
+ }
537
+ return result;
538
+ }
539
+
490
540
  // src/intersection.ts
491
541
  function intersection(...arrays) {
492
542
  const [first = [], ...rest] = arrays;
@@ -640,6 +690,20 @@ function map(collection, iteratee) {
640
690
  return result;
641
691
  }
642
692
 
693
+ // src/mapKeys.ts
694
+ function mapKeys(object, iteratee) {
695
+ if (!object) {
696
+ return {};
697
+ }
698
+ const result = {};
699
+ const keys2 = Object.keys(object);
700
+ for (let index = 0; index < keys2.length; index++) {
701
+ const key = keys2[index];
702
+ result[String(iteratee(object[key], key, object))] = object[key];
703
+ }
704
+ return result;
705
+ }
706
+
643
707
  // src/mapValues.ts
644
708
  function mapValues(object, iteratee) {
645
709
  if (!object) {
@@ -725,6 +789,23 @@ function pick(object, paths) {
725
789
  return result;
726
790
  }
727
791
 
792
+ // src/pickBy.ts
793
+ function pickBy(object, predicate) {
794
+ if (!object) {
795
+ return {};
796
+ }
797
+ const result = {};
798
+ const keys2 = Object.keys(object);
799
+ for (let index = 0; index < keys2.length; index++) {
800
+ const key = keys2[index];
801
+ const value = object[key];
802
+ if (predicate(value, key, object)) {
803
+ result[key] = value;
804
+ }
805
+ }
806
+ return result;
807
+ }
808
+
728
809
  // src/removeConsecutiveSlashesFromUrl.ts
729
810
  function removeConsecutiveSlashesFromUrl(url) {
730
811
  return url.replace(/\/{2,}/g, "/");
@@ -771,6 +852,42 @@ function set(object, path, value) {
771
852
  return setPath(object, path, value);
772
853
  }
773
854
 
855
+ // src/startCase.ts
856
+ function startCase(value) {
857
+ return words(value).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
858
+ }
859
+
860
+ // src/sum.ts
861
+ function sum(values) {
862
+ if (!Array.isArray(values)) {
863
+ return 0;
864
+ }
865
+ let result = 0;
866
+ for (let index = 0; index < values.length; index++) {
867
+ const value = values[index];
868
+ if (value !== void 0) {
869
+ result += value;
870
+ }
871
+ }
872
+ return result;
873
+ }
874
+
875
+ // src/sumBy.ts
876
+ function sumBy(collection, iteratee) {
877
+ if (!Array.isArray(collection)) {
878
+ return 0;
879
+ }
880
+ const callback = getIteratee(iteratee);
881
+ let result = 0;
882
+ for (let index = 0; index < collection.length; index++) {
883
+ const current = callback(collection[index], index, collection);
884
+ if (current !== void 0) {
885
+ result += current;
886
+ }
887
+ }
888
+ return result;
889
+ }
890
+
774
891
  // src/toAsyncFn.ts
775
892
  function toAsyncFn(fn, defaultValue) {
776
893
  if (!fn) return () => Promise.resolve(defaultValue);
@@ -801,6 +918,11 @@ function uniq(array) {
801
918
  }
802
919
  return baseUniq(array);
803
920
  }
921
+
922
+ // src/upperCase.ts
923
+ function upperCase(value) {
924
+ return words(value).join(" ").toUpperCase();
925
+ }
804
926
  // Annotate the CommonJS export names for ESM import in node:
805
927
  0 && (module.exports = {
806
928
  addLeadingSlash,
@@ -816,6 +938,7 @@ function uniq(array) {
816
938
  flattenDeep,
817
939
  forEach,
818
940
  get,
941
+ groupBy,
819
942
  intersection,
820
943
  intersectionBy,
821
944
  isArray,
@@ -834,6 +957,7 @@ function uniq(array) {
834
957
  isUndefined,
835
958
  keys,
836
959
  map,
960
+ mapKeys,
837
961
  mapValues,
838
962
  mapValuesAsync,
839
963
  noop,
@@ -843,10 +967,15 @@ function uniq(array) {
843
967
  padEnd,
844
968
  parseBooleanString,
845
969
  pick,
970
+ pickBy,
846
971
  reduce,
847
972
  removeConsecutiveSlashesFromUrl,
848
973
  set,
974
+ startCase,
975
+ sum,
976
+ sumBy,
849
977
  toAsyncFn,
850
978
  toStringRecord,
851
- uniq
979
+ uniq,
980
+ upperCase
852
981
  });
package/index.mjs CHANGED
@@ -24,6 +24,7 @@ function castArray(value) {
24
24
 
25
25
  // src/_internal.ts
26
26
  var pathPattern = /[^.[\]]+|\[(?:([^"'[\]]+)|["']([^"']+)["'])\]/g;
27
+ var unsafePathParts = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
27
28
  function isObject(value) {
28
29
  return value !== null && (typeof value === "object" || typeof value === "function");
29
30
  }
@@ -56,6 +57,15 @@ function toPath(path) {
56
57
  });
57
58
  return result.length > 0 ? result : [input];
58
59
  }
60
+ function hasUnsafePathPart(parts) {
61
+ for (let index = 0; index < parts.length; index++) {
62
+ const part = parts[index];
63
+ if (typeof part === "string" && unsafePathParts.has(part)) {
64
+ return true;
65
+ }
66
+ }
67
+ return false;
68
+ }
59
69
  function hasPath(value, path) {
60
70
  const parts = toPath(path);
61
71
  if (parts.length === 0) {
@@ -90,7 +100,7 @@ function setPath(target, path, value) {
90
100
  return target;
91
101
  }
92
102
  const parts = toPath(path);
93
- if (parts.length === 0) {
103
+ if (parts.length === 0 || hasUnsafePathPart(parts)) {
94
104
  return target;
95
105
  }
96
106
  let current = target;
@@ -111,7 +121,7 @@ function deletePath(target, path) {
111
121
  return;
112
122
  }
113
123
  const parts = toPath(path);
114
- if (parts.length === 0) {
124
+ if (parts.length === 0 || hasUnsafePathPart(parts)) {
115
125
  return;
116
126
  }
117
127
  let current = target;
@@ -157,6 +167,14 @@ function getIteratee(iteratee) {
157
167
  }
158
168
  return (value) => getPath(value, iteratee);
159
169
  }
170
+ var wordPattern = /[A-Z]{2,}(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]+|[0-9]+/g;
171
+ function words(value) {
172
+ const input = String(value ?? "");
173
+ if (input.length === 0) {
174
+ return [];
175
+ }
176
+ return input.replace(/[']/g, "").match(wordPattern) ?? [];
177
+ }
160
178
  function compareAscending(left, right) {
161
179
  if (sameValueZero(left, right)) {
162
180
  return 0;
@@ -417,6 +435,31 @@ function get(object, path, defaultValue) {
417
435
  return getPath(object, path, defaultValue);
418
436
  }
419
437
 
438
+ // src/groupBy.ts
439
+ function groupBy(collection, iteratee) {
440
+ if (!collection) {
441
+ return {};
442
+ }
443
+ const callback = getIteratee(iteratee);
444
+ const result = {};
445
+ if (Array.isArray(collection)) {
446
+ for (let index = 0; index < collection.length; index++) {
447
+ const value = collection[index];
448
+ const key = String(callback(value, index, collection));
449
+ (result[key] ??= []).push(value);
450
+ }
451
+ return result;
452
+ }
453
+ const keys2 = Object.keys(collection);
454
+ for (let index = 0; index < keys2.length; index++) {
455
+ const key = keys2[index];
456
+ const value = collection[key];
457
+ const group = String(callback(value, key, collection));
458
+ (result[group] ??= []).push(value);
459
+ }
460
+ return result;
461
+ }
462
+
420
463
  // src/intersection.ts
421
464
  function intersection(...arrays) {
422
465
  const [first = [], ...rest] = arrays;
@@ -570,6 +613,20 @@ function map(collection, iteratee) {
570
613
  return result;
571
614
  }
572
615
 
616
+ // src/mapKeys.ts
617
+ function mapKeys(object, iteratee) {
618
+ if (!object) {
619
+ return {};
620
+ }
621
+ const result = {};
622
+ const keys2 = Object.keys(object);
623
+ for (let index = 0; index < keys2.length; index++) {
624
+ const key = keys2[index];
625
+ result[String(iteratee(object[key], key, object))] = object[key];
626
+ }
627
+ return result;
628
+ }
629
+
573
630
  // src/mapValues.ts
574
631
  function mapValues(object, iteratee) {
575
632
  if (!object) {
@@ -655,6 +712,23 @@ function pick(object, paths) {
655
712
  return result;
656
713
  }
657
714
 
715
+ // src/pickBy.ts
716
+ function pickBy(object, predicate) {
717
+ if (!object) {
718
+ return {};
719
+ }
720
+ const result = {};
721
+ const keys2 = Object.keys(object);
722
+ for (let index = 0; index < keys2.length; index++) {
723
+ const key = keys2[index];
724
+ const value = object[key];
725
+ if (predicate(value, key, object)) {
726
+ result[key] = value;
727
+ }
728
+ }
729
+ return result;
730
+ }
731
+
658
732
  // src/removeConsecutiveSlashesFromUrl.ts
659
733
  function removeConsecutiveSlashesFromUrl(url) {
660
734
  return url.replace(/\/{2,}/g, "/");
@@ -701,6 +775,42 @@ function set(object, path, value) {
701
775
  return setPath(object, path, value);
702
776
  }
703
777
 
778
+ // src/startCase.ts
779
+ function startCase(value) {
780
+ return words(value).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
781
+ }
782
+
783
+ // src/sum.ts
784
+ function sum(values) {
785
+ if (!Array.isArray(values)) {
786
+ return 0;
787
+ }
788
+ let result = 0;
789
+ for (let index = 0; index < values.length; index++) {
790
+ const value = values[index];
791
+ if (value !== void 0) {
792
+ result += value;
793
+ }
794
+ }
795
+ return result;
796
+ }
797
+
798
+ // src/sumBy.ts
799
+ function sumBy(collection, iteratee) {
800
+ if (!Array.isArray(collection)) {
801
+ return 0;
802
+ }
803
+ const callback = getIteratee(iteratee);
804
+ let result = 0;
805
+ for (let index = 0; index < collection.length; index++) {
806
+ const current = callback(collection[index], index, collection);
807
+ if (current !== void 0) {
808
+ result += current;
809
+ }
810
+ }
811
+ return result;
812
+ }
813
+
704
814
  // src/toAsyncFn.ts
705
815
  function toAsyncFn(fn, defaultValue) {
706
816
  if (!fn) return () => Promise.resolve(defaultValue);
@@ -731,6 +841,11 @@ function uniq(array) {
731
841
  }
732
842
  return baseUniq(array);
733
843
  }
844
+
845
+ // src/upperCase.ts
846
+ function upperCase(value) {
847
+ return words(value).join(" ").toUpperCase();
848
+ }
734
849
  export {
735
850
  addLeadingSlash,
736
851
  arrayToRecord,
@@ -745,6 +860,7 @@ export {
745
860
  flattenDeep,
746
861
  forEach,
747
862
  get,
863
+ groupBy,
748
864
  intersection,
749
865
  intersectionBy,
750
866
  isArray,
@@ -763,6 +879,7 @@ export {
763
879
  isUndefined,
764
880
  keys,
765
881
  map,
882
+ mapKeys,
766
883
  mapValues,
767
884
  mapValuesAsync,
768
885
  noop,
@@ -772,10 +889,15 @@ export {
772
889
  padEnd,
773
890
  parseBooleanString,
774
891
  pick,
892
+ pickBy,
775
893
  reduce,
776
894
  removeConsecutiveSlashesFromUrl,
777
895
  set,
896
+ startCase,
897
+ sum,
898
+ sumBy,
778
899
  toAsyncFn,
779
900
  toStringRecord,
780
- uniq
901
+ uniq,
902
+ upperCase
781
903
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@web-ts-toolkit/utils",
3
3
  "description": "Shared collection, object, async, and URL helpers used across workspace packages",
4
4
  "homepage": "https://web-ts-toolkit.pages.dev/docs/packages/utils",
5
- "version": "0.28.0",
5
+ "version": "0.29.0",
6
6
  "sideEffects": false,
7
7
  "keywords": [
8
8
  "utilities",