@h3ravel/support 0.13.0 → 0.14.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/index.cjs +946 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +516 -65
- package/dist/index.d.ts +516 -65
- package/dist/index.js +937 -141
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -54,132 +54,27 @@ dayjs_plugin_timezone_js = __toESM(dayjs_plugin_timezone_js);
|
|
|
54
54
|
let dayjs_plugin_utc_js = require("dayjs/plugin/utc.js");
|
|
55
55
|
dayjs_plugin_utc_js = __toESM(dayjs_plugin_utc_js);
|
|
56
56
|
|
|
57
|
-
//#region src/
|
|
58
|
-
var Arr_exports = /* @__PURE__ */ __export({
|
|
59
|
-
alternate: () => alternate,
|
|
60
|
-
chunk: () => chunk,
|
|
61
|
-
collapse: () => collapse,
|
|
62
|
-
combine: () => combine,
|
|
63
|
-
find: () => find,
|
|
64
|
-
first: () => first,
|
|
65
|
-
flatten: () => flatten,
|
|
66
|
-
forget: () => forget,
|
|
67
|
-
isEmpty: () => isEmpty,
|
|
68
|
-
isNotEmpty: () => isNotEmpty,
|
|
69
|
-
last: () => last,
|
|
70
|
-
pop: () => pop,
|
|
71
|
-
prepend: () => prepend,
|
|
72
|
-
range: () => range,
|
|
73
|
-
reverse: () => reverse,
|
|
74
|
-
shift: () => shift,
|
|
75
|
-
take: () => take,
|
|
76
|
-
wrap: () => wrap
|
|
77
|
-
});
|
|
57
|
+
//#region src/Exceptions/InvalidArgumentException.ts
|
|
78
58
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* @template T - Type of elements in the array
|
|
82
|
-
* @param arr - The input array
|
|
83
|
-
* @param size - Size of each chunk (default: 2)
|
|
84
|
-
* @returns An array of chunks (arrays)
|
|
59
|
+
* Custom error for invalid type coercion
|
|
85
60
|
*/
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return chunks;
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* Collapse an array of arrays into a single array.
|
|
94
|
-
*/
|
|
95
|
-
const collapse = (arr) => {
|
|
96
|
-
const result = [];
|
|
97
|
-
for (const item of arr) if (Array.isArray(item)) result.push(...item);
|
|
98
|
-
else result.push(item);
|
|
99
|
-
return result;
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Alternates between two arrays, creating a zipped result.
|
|
103
|
-
*/
|
|
104
|
-
const alternate = (a, b) => {
|
|
105
|
-
const result = [];
|
|
106
|
-
const max = Math.max(a.length, b.length);
|
|
107
|
-
for (let i = 0; i < max; i++) {
|
|
108
|
-
if (i < a.length) result.push(a[i]);
|
|
109
|
-
if (i < b.length) result.push(b[i]);
|
|
61
|
+
var InvalidArgumentException = class extends Error {
|
|
62
|
+
constructor(message) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "InvalidArgumentException";
|
|
110
65
|
}
|
|
111
|
-
return result;
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* Combine arrays and sum their values element by element.
|
|
115
|
-
*/
|
|
116
|
-
const combine = (...arr) => {
|
|
117
|
-
const maxLength = Math.max(...arr.map((a) => a.length));
|
|
118
|
-
const result = new Array(maxLength).fill(0);
|
|
119
|
-
for (let i = 0; i < maxLength; i++) for (const array of arr) result[i] += array[i] || 0;
|
|
120
|
-
return result;
|
|
121
|
-
};
|
|
122
|
-
/** Find the value associated with a given key. */
|
|
123
|
-
const find = (key, arr) => arr.find((item) => item === key) || null;
|
|
124
|
-
/** Returns a new array without the given indices. */
|
|
125
|
-
const forget = (arr, keys) => arr.filter((_, i) => !keys.includes(i));
|
|
126
|
-
/** Remove the first element and return tuple [el, rest]. */
|
|
127
|
-
const first = (arr) => {
|
|
128
|
-
if (!arr.length) throw new Error("Cannot shift from empty array");
|
|
129
|
-
return [arr[0], arr.slice(1)];
|
|
130
|
-
};
|
|
131
|
-
/** Remove the last element and return tuple [el, rest]. */
|
|
132
|
-
const last = (arr) => {
|
|
133
|
-
if (!arr.length) throw new Error("Cannot pop from empty array");
|
|
134
|
-
return [arr[arr.length - 1], arr.slice(0, -1)];
|
|
135
|
-
};
|
|
136
|
-
/** Check if array is empty. */
|
|
137
|
-
const isEmpty = (arr) => {
|
|
138
|
-
if (arr.length === 0) return true;
|
|
139
|
-
else return false;
|
|
140
|
-
};
|
|
141
|
-
/** Check if array is empty. */
|
|
142
|
-
const isNotEmpty = (arr) => arr.length > 0;
|
|
143
|
-
/** Pop the element off the end of array. */
|
|
144
|
-
const pop = (arr) => arr.slice(0, -1);
|
|
145
|
-
/** Add elements to the beginning of array. */
|
|
146
|
-
const prepend = (arr, ...elements) => [...elements, ...arr];
|
|
147
|
-
/** Take first n elements of array. */
|
|
148
|
-
const take = (amount, arr) => arr.slice(0, Math.max(0, amount));
|
|
149
|
-
/** Create a new array in reverse order. */
|
|
150
|
-
const reverse = (arr) => [...arr].reverse();
|
|
151
|
-
/** Alias for first element removal. */
|
|
152
|
-
const shift = first;
|
|
153
|
-
/**
|
|
154
|
-
* Generates an array of sequential numbers.
|
|
155
|
-
*
|
|
156
|
-
* @param size - Number of elements in the range
|
|
157
|
-
* @param startAt - Starting number (default: 0)
|
|
158
|
-
* @returns An array of numbers from startAt to startAt + size - 1
|
|
159
|
-
*/
|
|
160
|
-
const range = (size, startAt = 0) => {
|
|
161
|
-
if (size <= 0 || !Number.isFinite(size)) return [];
|
|
162
|
-
return Array.from({ length: size }, (_, i) => startAt + i);
|
|
163
|
-
};
|
|
164
|
-
/** Flatten multi-dimensional arrays into single level. */
|
|
165
|
-
const flatten = (arr) => {
|
|
166
|
-
const result = [];
|
|
167
|
-
const recurse = (input) => {
|
|
168
|
-
for (const item of input) if (Array.isArray(item)) recurse(item);
|
|
169
|
-
else result.push(item);
|
|
170
|
-
};
|
|
171
|
-
recurse(arr);
|
|
172
|
-
return result;
|
|
173
66
|
};
|
|
67
|
+
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/Exceptions/RuntimeException.ts
|
|
174
70
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* @param {Array} value
|
|
178
|
-
* @return array
|
|
71
|
+
* Custom error for invalid type coercion
|
|
179
72
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
73
|
+
var RuntimeException = class extends Error {
|
|
74
|
+
constructor(message) {
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = "RuntimeException";
|
|
77
|
+
}
|
|
183
78
|
};
|
|
184
79
|
|
|
185
80
|
//#endregion
|
|
@@ -346,10 +241,10 @@ const verifyChecksum = (data, expectedChecksum, algorithm = "sha256") => {
|
|
|
346
241
|
* @param shift - Number of positions to shift (default: 13)
|
|
347
242
|
* @returns Encrypted/decrypted text
|
|
348
243
|
*/
|
|
349
|
-
const caesarCipher = (text, shift
|
|
244
|
+
const caesarCipher = (text, shift = 13) => {
|
|
350
245
|
return text.replace(/[a-zA-Z]/g, (char) => {
|
|
351
246
|
const baseCharCode = char === char.toUpperCase() ? "A" : "a";
|
|
352
|
-
const shiftedCharCode = (char.charCodeAt(0) - baseCharCode.charCodeAt(0) + shift
|
|
247
|
+
const shiftedCharCode = (char.charCodeAt(0) - baseCharCode.charCodeAt(0) + shift) % 26;
|
|
353
248
|
const newCharCode = (shiftedCharCode < 0 ? 26 + shiftedCharCode : shiftedCharCode) + baseCharCode.charCodeAt(0);
|
|
354
249
|
return String.fromCharCode(newCharCode);
|
|
355
250
|
});
|
|
@@ -561,22 +456,27 @@ const toHumanTime = (seconds = 0, worded = false) => {
|
|
|
561
456
|
if (secs || !hours && !minutes) parts.push(`${secs}sec`);
|
|
562
457
|
return parts.join(" ");
|
|
563
458
|
}
|
|
564
|
-
|
|
565
|
-
const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ":";
|
|
566
|
-
const ss = secs < 10 ? `0${secs}` : secs;
|
|
567
|
-
return `${hh}${mm}${ss}`;
|
|
459
|
+
return `${hours > 0 ? `${hours}:` : ""}${(hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ":"}${secs < 10 ? `0${secs}` : secs}`;
|
|
568
460
|
};
|
|
569
461
|
|
|
570
462
|
//#endregion
|
|
571
463
|
//#region src/Helpers/Obj.ts
|
|
572
464
|
var Obj_exports = /* @__PURE__ */ __export({
|
|
465
|
+
Obj: () => Obj,
|
|
466
|
+
data_fill: () => data_fill,
|
|
467
|
+
data_forget: () => data_forget,
|
|
468
|
+
data_get: () => data_get,
|
|
469
|
+
data_set: () => data_set,
|
|
573
470
|
dot: () => dot,
|
|
574
471
|
extractProperties: () => extractProperties,
|
|
575
472
|
getValue: () => getValue,
|
|
576
473
|
modObj: () => modObj,
|
|
577
474
|
safeDot: () => safeDot,
|
|
578
475
|
setNested: () => setNested,
|
|
579
|
-
slugifyKeys: () => slugifyKeys
|
|
476
|
+
slugifyKeys: () => slugifyKeys,
|
|
477
|
+
toCssClasses: () => toCssClasses,
|
|
478
|
+
toCssStyles: () => toCssStyles,
|
|
479
|
+
undot: () => undot
|
|
580
480
|
});
|
|
581
481
|
/**
|
|
582
482
|
* Flattens a nested object into a single-level object
|
|
@@ -719,6 +619,893 @@ const slugifyKeys = (obj, only = [], separator = "_") => {
|
|
|
719
619
|
if (only.length) entries = entries.filter(([key]) => only.includes(key));
|
|
720
620
|
return Object.fromEntries(entries.map(([key, value]) => [slugify(key), value]));
|
|
721
621
|
};
|
|
622
|
+
/**
|
|
623
|
+
* toCssClasses
|
|
624
|
+
*
|
|
625
|
+
* Convert array/object/string input into a CSS class string.
|
|
626
|
+
* - Arrays: included if truthy
|
|
627
|
+
* - Objects: keys included if value is truthy
|
|
628
|
+
* - Strings: included as-is
|
|
629
|
+
*/
|
|
630
|
+
function toCssClasses(input) {
|
|
631
|
+
if (!input) return "";
|
|
632
|
+
const classes = [];
|
|
633
|
+
if (typeof input === "string") return input.trim();
|
|
634
|
+
if (Array.isArray(input)) input.forEach((item) => {
|
|
635
|
+
if (item) classes.push(String(item).trim());
|
|
636
|
+
});
|
|
637
|
+
else if (typeof input === "object") {
|
|
638
|
+
for (const [key, value] of Object.entries(input)) if (value) classes.push(key);
|
|
639
|
+
}
|
|
640
|
+
return classes.join(" ");
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* toCssStyles
|
|
644
|
+
*
|
|
645
|
+
* Convert object input into CSS style string.
|
|
646
|
+
* - Only includes truthy values (ignores null/undefined/false)
|
|
647
|
+
*/
|
|
648
|
+
function toCssStyles(styles) {
|
|
649
|
+
const parts = [];
|
|
650
|
+
for (const [k, v] of Object.entries(styles)) {
|
|
651
|
+
if (v === null || v === void 0 || v === false) continue;
|
|
652
|
+
parts.push(`${k}:${v}`);
|
|
653
|
+
}
|
|
654
|
+
return parts.join(";");
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* undot
|
|
658
|
+
*
|
|
659
|
+
* Convert a dot-notated object back into nested structure.
|
|
660
|
+
*
|
|
661
|
+
* Example:
|
|
662
|
+
* undot({ 'a.b': 1, 'c.0': 2 }) -> { a: { b: 1 }, c: [2] }
|
|
663
|
+
*/
|
|
664
|
+
function undot(obj) {
|
|
665
|
+
const result = {};
|
|
666
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
667
|
+
const parts = key.split(".");
|
|
668
|
+
let node = result;
|
|
669
|
+
for (let i = 0; i < parts.length; i++) {
|
|
670
|
+
const part = parts[i];
|
|
671
|
+
const isLast = i === parts.length - 1;
|
|
672
|
+
const nextPart = parts[i + 1];
|
|
673
|
+
const isArrayIndex = !isNaN(Number(nextPart));
|
|
674
|
+
if (isLast) node[part] = value;
|
|
675
|
+
else {
|
|
676
|
+
if (!(part in node)) node[part] = isArrayIndex ? [] : {};
|
|
677
|
+
node = node[part];
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* data_get
|
|
685
|
+
*
|
|
686
|
+
* Get a value from an object using dot notation.
|
|
687
|
+
*/
|
|
688
|
+
function data_get(obj, path, defaultValue) {
|
|
689
|
+
if (!obj) return defaultValue;
|
|
690
|
+
const parts = Array.isArray(path) ? path : path.split(".");
|
|
691
|
+
let current = obj;
|
|
692
|
+
for (const part of parts) {
|
|
693
|
+
if (!current || !(part in current)) return defaultValue;
|
|
694
|
+
current = current[part];
|
|
695
|
+
}
|
|
696
|
+
return current;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* data_set
|
|
700
|
+
*
|
|
701
|
+
* Set a value in an object using dot notation. Mutates the object.
|
|
702
|
+
*/
|
|
703
|
+
function data_set(obj, path, value) {
|
|
704
|
+
const parts = Array.isArray(path) ? path : path.split(".");
|
|
705
|
+
let current = obj;
|
|
706
|
+
for (let i = 0; i < parts.length; i++) {
|
|
707
|
+
const part = parts[i];
|
|
708
|
+
if (i === parts.length - 1) current[part] = value;
|
|
709
|
+
else {
|
|
710
|
+
if (!(part in current) || typeof current[part] !== "object" || current[part] === null) current[part] = {};
|
|
711
|
+
current = current[part];
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* data_fill
|
|
717
|
+
*
|
|
718
|
+
* Like data_set, but only sets the value if the key does NOT exist.
|
|
719
|
+
*/
|
|
720
|
+
function data_fill(obj, path, value) {
|
|
721
|
+
if (data_get(obj, path) === void 0) data_set(obj, path, value);
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* data_forget
|
|
725
|
+
*
|
|
726
|
+
* Remove a key from an object using dot notation.
|
|
727
|
+
*/
|
|
728
|
+
function data_forget(obj, path) {
|
|
729
|
+
const parts = Array.isArray(path) ? path : path.split(".");
|
|
730
|
+
let current = obj;
|
|
731
|
+
for (let i = 0; i < parts.length; i++) {
|
|
732
|
+
const part = parts[i];
|
|
733
|
+
if (i === parts.length - 1) delete current[part];
|
|
734
|
+
else {
|
|
735
|
+
if (!current[part] || typeof current[part] !== "object") break;
|
|
736
|
+
current = current[part];
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
var Obj = class Obj {
|
|
741
|
+
/**
|
|
742
|
+
* Check if the value is a non-null object (associative/accessible).
|
|
743
|
+
*/
|
|
744
|
+
static accessible(value) {
|
|
745
|
+
return value !== null && typeof value === "object";
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Add a key-value pair to an object only if the key does not already exist.
|
|
749
|
+
*
|
|
750
|
+
* Returns a new object (does not mutate original).
|
|
751
|
+
*/
|
|
752
|
+
static add(obj, key, value) {
|
|
753
|
+
if (!(key in obj)) return {
|
|
754
|
+
...obj,
|
|
755
|
+
[key]: value
|
|
756
|
+
};
|
|
757
|
+
return obj;
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Split object into [keys, values]
|
|
761
|
+
*/
|
|
762
|
+
static divide(obj) {
|
|
763
|
+
return [Object.keys(obj), Object.values(obj)];
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Check if a key exists in the object.
|
|
767
|
+
*/
|
|
768
|
+
static exists(obj, key) {
|
|
769
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get a value from an object using dot notation.
|
|
773
|
+
*
|
|
774
|
+
* Example:
|
|
775
|
+
* Obj.get({a:{b:1}}, 'a.b') -> 1
|
|
776
|
+
*/
|
|
777
|
+
static get(obj, path, defaultValue) {
|
|
778
|
+
if (!Obj.accessible(obj)) return defaultValue;
|
|
779
|
+
const parts = Array.isArray(path) ? path : path.split(".");
|
|
780
|
+
let current = obj;
|
|
781
|
+
for (const part of parts) {
|
|
782
|
+
if (!Obj.accessible(current) || !(part in current)) return defaultValue;
|
|
783
|
+
current = current[part];
|
|
784
|
+
}
|
|
785
|
+
return current;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Check if the object has a given key or keys (dot notation supported).
|
|
789
|
+
*/
|
|
790
|
+
static has(obj, keys) {
|
|
791
|
+
if (!Obj.accessible(obj)) return false;
|
|
792
|
+
return (Array.isArray(keys) ? keys : [keys]).every((key) => {
|
|
793
|
+
const parts = key.split(".");
|
|
794
|
+
let current = obj;
|
|
795
|
+
for (const part of parts) {
|
|
796
|
+
if (!Obj.accessible(current) || !(part in current)) return false;
|
|
797
|
+
current = current[part];
|
|
798
|
+
}
|
|
799
|
+
return true;
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Check if an object is associative (has at least one non-numeric key).
|
|
804
|
+
*/
|
|
805
|
+
static isAssoc(obj) {
|
|
806
|
+
if (!Obj.accessible(obj)) return false;
|
|
807
|
+
return Object.keys(obj).some((k) => isNaN(Number(k)));
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Add a prefix to all keys of the object.
|
|
811
|
+
*/
|
|
812
|
+
static prependKeysWith(obj, prefix) {
|
|
813
|
+
if (!Obj.accessible(obj)) return {};
|
|
814
|
+
const result = {};
|
|
815
|
+
for (const [k, v] of Object.entries(obj)) result[`${prefix}${k}`] = v;
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Convert an object into a URL query string.
|
|
820
|
+
*
|
|
821
|
+
* Nested objects/arrays are flattened using bracket notation.
|
|
822
|
+
*/
|
|
823
|
+
static query(obj) {
|
|
824
|
+
const encode = encodeURIComponent;
|
|
825
|
+
const parts = [];
|
|
826
|
+
function build(key, value) {
|
|
827
|
+
if (Array.isArray(value)) value.forEach((v, i) => build(`${key}[${i}]`, v));
|
|
828
|
+
else if (Obj.accessible(value)) Object.entries(value).forEach(([k, v]) => build(`${key}[${k}]`, v));
|
|
829
|
+
else parts.push(`${encode(key)}=${encode(value)}`);
|
|
830
|
+
}
|
|
831
|
+
Object.entries(obj).forEach(([k, v]) => build(k, v));
|
|
832
|
+
return parts.join("&");
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/Helpers/Arr.ts
|
|
838
|
+
/**
|
|
839
|
+
* Arr — Laravel-like array helpers for JavaScript.
|
|
840
|
+
*
|
|
841
|
+
* - Methods aim for clear, predictable JS behavior.
|
|
842
|
+
* - Inputs are validated where useful; functions try not to mutate arguments.
|
|
843
|
+
*/
|
|
844
|
+
var Arr = class Arr {
|
|
845
|
+
/**
|
|
846
|
+
* Helper: is a value an object (but not null).
|
|
847
|
+
*/
|
|
848
|
+
static _isObject(value) {
|
|
849
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Helper: deep clone for safety (simple).
|
|
853
|
+
* Uses JSON methods — good for typical data shapes (no functions, Dates, Maps).
|
|
854
|
+
*/
|
|
855
|
+
static _clone(value) {
|
|
856
|
+
try {
|
|
857
|
+
return JSON.parse(JSON.stringify(value));
|
|
858
|
+
} catch {
|
|
859
|
+
return value;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Retrieve a value using dot notation
|
|
864
|
+
* Throws if value is not an array
|
|
865
|
+
*/
|
|
866
|
+
static array(obj, path, defaultValue) {
|
|
867
|
+
const val = data_get(obj, path, defaultValue);
|
|
868
|
+
if (!Array.isArray(val)) throw new InvalidArgumentException(`Value at "${path}" is not an array.`);
|
|
869
|
+
return val;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Retrieve a value using dot notation
|
|
873
|
+
* Throws if value is not a boolean
|
|
874
|
+
*/
|
|
875
|
+
static boolean(obj, path, defaultValue) {
|
|
876
|
+
const val = data_get(obj, path, defaultValue);
|
|
877
|
+
if (typeof val !== "boolean") throw new InvalidArgumentException(`Value at "${path}" is not a boolean.`);
|
|
878
|
+
return val;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Flatten an array of arrays by one level.
|
|
882
|
+
*
|
|
883
|
+
* Example:
|
|
884
|
+
* Arr.collapse([[1,2], [3], 4]) -> [1,2,3,4]
|
|
885
|
+
*/
|
|
886
|
+
static collapse(array) {
|
|
887
|
+
if (!Array.isArray(array)) return [];
|
|
888
|
+
const result = [];
|
|
889
|
+
for (const item of array) if (Array.isArray(item)) result.push(...item);
|
|
890
|
+
else result.push(item);
|
|
891
|
+
return result;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Cartesian product of arrays.
|
|
895
|
+
*
|
|
896
|
+
* Example:
|
|
897
|
+
* Arr.crossJoin([1,2], ['a','b']) -> [[1,'a'], [1,'b'], [2,'a'], [2,'b']]
|
|
898
|
+
*
|
|
899
|
+
* Accepts any number of array arguments (or single array-of-arrays).
|
|
900
|
+
*/
|
|
901
|
+
static crossJoin(...arrays) {
|
|
902
|
+
let inputs = arrays;
|
|
903
|
+
if (arrays.length === 1 && Array.isArray(arrays[0]) && arrays[0].some(Array.isArray)) inputs = arrays[0];
|
|
904
|
+
inputs = inputs.map((a) => Array.isArray(a) ? a : [a]);
|
|
905
|
+
let product = [[]];
|
|
906
|
+
for (const arr of inputs) {
|
|
907
|
+
const next = [];
|
|
908
|
+
for (const prefix of product) for (const value of arr) next.push([...prefix, value]);
|
|
909
|
+
product = next;
|
|
910
|
+
}
|
|
911
|
+
return product;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Split an array (or object) into two arrays: [keys, values].
|
|
915
|
+
*
|
|
916
|
+
* For arrays, keys are numeric indices. For objects, keys are property names.
|
|
917
|
+
*
|
|
918
|
+
* Example:
|
|
919
|
+
* Arr.divide(['a','b']) -> [[0,1], ['a','b']]
|
|
920
|
+
* Arr.divide({x:1,y:2}) -> [['x','y'], [1,2]]
|
|
921
|
+
*/
|
|
922
|
+
static divide(input) {
|
|
923
|
+
if (Array.isArray(input)) return [input.map((_, i) => i), input.slice()];
|
|
924
|
+
if (Arr._isObject(input)) {
|
|
925
|
+
const keys = Object.keys(input);
|
|
926
|
+
return [keys, keys.map((k) => input[k])];
|
|
927
|
+
}
|
|
928
|
+
return [[], []];
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Flatten a nested array/object structure into a single-level object
|
|
932
|
+
* with dot-notated keys.
|
|
933
|
+
*
|
|
934
|
+
* Example:
|
|
935
|
+
* Arr.dot({ a: { b: 1 }, c: [2,3] }) -> { 'a.b': 1, 'c.0': 2, 'c.1': 3 }
|
|
936
|
+
*
|
|
937
|
+
* Works for arrays and plain objects.
|
|
938
|
+
*/
|
|
939
|
+
static dot(input, prefix = "") {
|
|
940
|
+
const result = {};
|
|
941
|
+
const recurse = (val, path) => {
|
|
942
|
+
if (Array.isArray(val)) for (let i = 0; i < val.length; i++) recurse(val[i], path ? `${path}.${i}` : String(i));
|
|
943
|
+
else if (Arr._isObject(val)) for (const [k, v] of Object.entries(val)) recurse(v, path ? `${path}.${k}` : k);
|
|
944
|
+
else result[path] = val;
|
|
945
|
+
};
|
|
946
|
+
if (Array.isArray(input) || Arr._isObject(input)) recurse(input, prefix);
|
|
947
|
+
return result;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Checks if all elements satisfy the predicate
|
|
951
|
+
*/
|
|
952
|
+
static every(array, predicate) {
|
|
953
|
+
return array.every(predicate);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Remove items by keys/indices from an array or properties from an object.
|
|
957
|
+
*
|
|
958
|
+
* For arrays: keys are numeric indices (single number or array of numbers).
|
|
959
|
+
*
|
|
960
|
+
* Returns a shallow-copied result (does not mutate input).
|
|
961
|
+
*
|
|
962
|
+
* Example:
|
|
963
|
+
* Arr.except([10,20,30], [1]) -> [10,30]
|
|
964
|
+
*/
|
|
965
|
+
static except(input, keys) {
|
|
966
|
+
const keySet = new Set(Array.isArray(keys) ? keys : [keys]);
|
|
967
|
+
if (Array.isArray(input)) return input.filter((_, idx) => !keySet.has(idx));
|
|
968
|
+
if (Arr._isObject(input)) {
|
|
969
|
+
const out = {};
|
|
970
|
+
for (const [k, v] of Object.entries(input)) if (!keySet.has(k)) out[k] = v;
|
|
971
|
+
return out;
|
|
972
|
+
}
|
|
973
|
+
return input;
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Return the first element of an array that satisfies the predicate,
|
|
977
|
+
* or the first element if no predicate is provided, otherwise the defaultValue.
|
|
978
|
+
*
|
|
979
|
+
* Predicate can be true (boolean), a function or a value to match (strict equality).
|
|
980
|
+
*
|
|
981
|
+
* When predicate is true (boolean), the first element will be removed and a tuple will be returned [el, rest].
|
|
982
|
+
*
|
|
983
|
+
* @param array
|
|
984
|
+
* @param predicate
|
|
985
|
+
* @param defaultValue
|
|
986
|
+
*
|
|
987
|
+
* @returns
|
|
988
|
+
*/
|
|
989
|
+
static first(array, predicate, defaultValue) {
|
|
990
|
+
if (predicate === true) {
|
|
991
|
+
if (!array.length) throw new Error("Cannot shift from empty array");
|
|
992
|
+
return [array[0], array.slice(1)];
|
|
993
|
+
}
|
|
994
|
+
if (!Array.isArray(array) || array.length === 0) return defaultValue;
|
|
995
|
+
if (predicate === void 0) return array[0];
|
|
996
|
+
if (typeof predicate === "function") {
|
|
997
|
+
for (const item of array) if (predicate(item)) return item;
|
|
998
|
+
return defaultValue;
|
|
999
|
+
}
|
|
1000
|
+
for (const item of array) if (typeof predicate !== "boolean" && item === predicate) return item;
|
|
1001
|
+
return defaultValue;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Recursively flatten an array up to `depth` levels (default: Infinity).
|
|
1005
|
+
*
|
|
1006
|
+
* Example:
|
|
1007
|
+
* Arr.flatten([1, [2, [3]]], 1) -> [1,2,[3]]
|
|
1008
|
+
*/
|
|
1009
|
+
static flatten(array, depth = Infinity) {
|
|
1010
|
+
if (!Array.isArray(array)) return [];
|
|
1011
|
+
if (depth < 1) return array.slice();
|
|
1012
|
+
const result = [];
|
|
1013
|
+
for (const item of array) if (Array.isArray(item) && depth > 0) result.push(...Arr.flatten(item, depth - 1));
|
|
1014
|
+
else result.push(item);
|
|
1015
|
+
return result;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Retrieve a value from an array/object using dot notation
|
|
1019
|
+
* Throws if value is not a float
|
|
1020
|
+
*/
|
|
1021
|
+
static float(obj, path, defaultValue) {
|
|
1022
|
+
const val = data_get(obj, path, defaultValue);
|
|
1023
|
+
if (typeof val !== "number" || Number.isNaN(val)) throw new InvalidArgumentException(`Value at "${path}" is not a float.`);
|
|
1024
|
+
return val;
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Remove element(s) by index or dot-notated path from an array/object.
|
|
1028
|
+
*
|
|
1029
|
+
* For arrays: accepts numeric index or array of indices. Returns a new array.
|
|
1030
|
+
*
|
|
1031
|
+
* For objects: supports dot notation to remove nested keys.
|
|
1032
|
+
*
|
|
1033
|
+
* Example:
|
|
1034
|
+
* Arr.forget([1,2,3], 1) -> [1,3]
|
|
1035
|
+
* Arr.forget({a:{b:1}}, 'a.b') -> { a: {} }
|
|
1036
|
+
*/
|
|
1037
|
+
static forget(input, keys) {
|
|
1038
|
+
if (Array.isArray(input)) {
|
|
1039
|
+
const removeSet = new Set(Array.isArray(keys) ? keys : [keys]);
|
|
1040
|
+
return input.filter((_, i) => !removeSet.has(i));
|
|
1041
|
+
}
|
|
1042
|
+
if (Arr._isObject(input)) {
|
|
1043
|
+
const out = Arr._clone(input);
|
|
1044
|
+
const keyList = Array.isArray(keys) ? keys : [keys];
|
|
1045
|
+
for (const key of keyList) {
|
|
1046
|
+
const parts = String(key).split(".");
|
|
1047
|
+
let node = out;
|
|
1048
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1049
|
+
const part = parts[i];
|
|
1050
|
+
if (i === parts.length - 1) {
|
|
1051
|
+
if (node && Object.prototype.hasOwnProperty.call(node, part)) delete node[part];
|
|
1052
|
+
} else {
|
|
1053
|
+
if (!Arr._isObject(node[part])) break;
|
|
1054
|
+
node = node[part];
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return out;
|
|
1059
|
+
}
|
|
1060
|
+
return input;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Converts various input types into a plain array
|
|
1064
|
+
* Supports Arrays, Objects, Iterables, Map, WeakMap, and custom toArray/toJSON/jsonSerialize methods
|
|
1065
|
+
*/
|
|
1066
|
+
static from(value) {
|
|
1067
|
+
if (value == null) return [];
|
|
1068
|
+
if (Array.isArray(value)) return value;
|
|
1069
|
+
if (Symbol.iterator in Object(value)) return [...value];
|
|
1070
|
+
if (value.toArray) return value.toArray();
|
|
1071
|
+
if (value.toJSON) return value.toJSON();
|
|
1072
|
+
if (value.jsonSerialize) return value.jsonSerialize();
|
|
1073
|
+
if (value instanceof Map) return Array.from(value.entries());
|
|
1074
|
+
if (value instanceof WeakMap) return [];
|
|
1075
|
+
if (typeof value === "object") return Object.values(value);
|
|
1076
|
+
return [value];
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Checks if an object has all the specified keys
|
|
1080
|
+
*/
|
|
1081
|
+
static hasAll(obj, keys) {
|
|
1082
|
+
return keys.every((k) => k in obj);
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* For arrays: check if the array contains any of the provided values.
|
|
1086
|
+
*
|
|
1087
|
+
* values can be single value or array of values.
|
|
1088
|
+
*
|
|
1089
|
+
* Example:
|
|
1090
|
+
* Arr.hasAny([1,2,3], [4,2]) -> true
|
|
1091
|
+
*/
|
|
1092
|
+
static hasAny(array, values) {
|
|
1093
|
+
if (!Array.isArray(array)) return false;
|
|
1094
|
+
const vs = Array.isArray(values) ? values : [values];
|
|
1095
|
+
const set = new Set(array);
|
|
1096
|
+
for (const v of vs) if (set.has(v)) return true;
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Retrieve a value using dot notation
|
|
1101
|
+
* Throws if value is not an integer
|
|
1102
|
+
*/
|
|
1103
|
+
static integer(obj, path, defaultValue) {
|
|
1104
|
+
const val = data_get(obj, path, defaultValue);
|
|
1105
|
+
if (!Number.isInteger(val)) throw new InvalidArgumentException(`Value at "${path}" is not an integer.`);
|
|
1106
|
+
return val;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Determine if the input is a "list-like" array: an Array with
|
|
1110
|
+
* contiguous numeric indices starting at 0 (no gaps).
|
|
1111
|
+
*
|
|
1112
|
+
* Example:
|
|
1113
|
+
* Arr.isList([1,2,3]) -> true
|
|
1114
|
+
* const a = []; a[2] = 5; Arr.isList(a) -> false
|
|
1115
|
+
*/
|
|
1116
|
+
static isList(value) {
|
|
1117
|
+
if (!Array.isArray(value)) return false;
|
|
1118
|
+
for (let i = 0; i < value.length; i++) if (!(i in value)) return false;
|
|
1119
|
+
return true;
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Join array elements into a string using the given separator.
|
|
1123
|
+
*
|
|
1124
|
+
* Example:
|
|
1125
|
+
* Arr.join([1,2,3], '-') -> '1-2-3'
|
|
1126
|
+
*/
|
|
1127
|
+
static join(array, separator = ",") {
|
|
1128
|
+
return Array.isArray(array) ? array.join(separator) : "";
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* Create an object indexed by a key or callback function.
|
|
1132
|
+
*
|
|
1133
|
+
* Example:
|
|
1134
|
+
* Arr.keyBy([{id:1},{id:2}], 'id') -> { '1': {id:1}, '2': {id:2} }
|
|
1135
|
+
*/
|
|
1136
|
+
static keyBy(array, key) {
|
|
1137
|
+
const result = {};
|
|
1138
|
+
if (!Array.isArray(array)) return result;
|
|
1139
|
+
for (const item of array) {
|
|
1140
|
+
let k;
|
|
1141
|
+
if (typeof key === "function") k = key(item);
|
|
1142
|
+
else k = String(item[key]);
|
|
1143
|
+
result[k] = item;
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Get the last element of an array, optionally matching a predicate,
|
|
1149
|
+
* or the last element if no predicate is provided, otherwise the defaultValue.
|
|
1150
|
+
*
|
|
1151
|
+
* Predicate can be a true (boolean), a function or a value to match (strict equality).
|
|
1152
|
+
*
|
|
1153
|
+
* When predicate is true (boolean), the last element will be removed and a tuple will be returned [el, rest].
|
|
1154
|
+
*
|
|
1155
|
+
* @param array
|
|
1156
|
+
* @param predicate
|
|
1157
|
+
* @param defaultValue
|
|
1158
|
+
*
|
|
1159
|
+
* @returns
|
|
1160
|
+
*/
|
|
1161
|
+
static last(array, predicate, defaultValue) {
|
|
1162
|
+
if (predicate === true) {
|
|
1163
|
+
if (!array.length) throw new Error("Cannot pop from empty array");
|
|
1164
|
+
return [array[array.length - 1], array.slice(0, -1)];
|
|
1165
|
+
}
|
|
1166
|
+
if (!Array.isArray(array) || array.length === 0) return defaultValue;
|
|
1167
|
+
if (!predicate) return array[array.length - 1];
|
|
1168
|
+
if (typeof predicate === "function") {
|
|
1169
|
+
for (let i = array.length - 1; i >= 0; i--) if (predicate(array[i])) return array[i];
|
|
1170
|
+
} else for (let i = array.length - 1; i >= 0; i--) if (array[i] === predicate) return array[i];
|
|
1171
|
+
return defaultValue;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Transform each element in an array using a callback.
|
|
1175
|
+
*/
|
|
1176
|
+
static map(array, callback) {
|
|
1177
|
+
return Array.isArray(array) ? array.map(callback) : [];
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Maps a multi-dimensional array with a spread callback
|
|
1181
|
+
*/
|
|
1182
|
+
static mapSpread(array, callback) {
|
|
1183
|
+
return array.map((item) => callback(...Array.isArray(item) ? item : [item]));
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Map each element to a key-value pair.
|
|
1187
|
+
*
|
|
1188
|
+
* Example:
|
|
1189
|
+
* Arr.mapWithKeys([{id:1, name:'A'}], x => [x.id, x.name])
|
|
1190
|
+
* -> { '1': 'A' }
|
|
1191
|
+
*/
|
|
1192
|
+
static mapWithKeys(array, callback) {
|
|
1193
|
+
const result = {};
|
|
1194
|
+
if (!Array.isArray(array)) return result;
|
|
1195
|
+
array.forEach((item, idx) => {
|
|
1196
|
+
const [k, v] = callback(item, idx);
|
|
1197
|
+
result[String(k)] = v;
|
|
1198
|
+
});
|
|
1199
|
+
return result;
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Return only elements at the given indices.
|
|
1203
|
+
*
|
|
1204
|
+
* Example:
|
|
1205
|
+
* Arr.only([10,20,30], [0,2]) -> [10,30]
|
|
1206
|
+
*/
|
|
1207
|
+
static only(array, keys) {
|
|
1208
|
+
if (!Array.isArray(array)) return [];
|
|
1209
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
1210
|
+
return array.filter((_, idx) => keyArray.includes(idx));
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Split an array into two arrays based on a predicate
|
|
1214
|
+
*/
|
|
1215
|
+
static partition(array, predicate) {
|
|
1216
|
+
const truthy = [];
|
|
1217
|
+
const falsy = [];
|
|
1218
|
+
array.forEach((item) => (predicate(item) ? truthy : falsy).push(item));
|
|
1219
|
+
return [truthy, falsy];
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Extract a property from each element in an array of objects.
|
|
1223
|
+
*
|
|
1224
|
+
* Example:
|
|
1225
|
+
* Arr.pluck([{name:'A'},{name:'B'}], 'name') -> ['A','B']
|
|
1226
|
+
*/
|
|
1227
|
+
static pluck(array, key) {
|
|
1228
|
+
if (!Array.isArray(array)) return [];
|
|
1229
|
+
return array.map((item) => item[key]);
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Add elements to the beginning of an array and return a new array.
|
|
1233
|
+
*
|
|
1234
|
+
* @param array
|
|
1235
|
+
* @param value
|
|
1236
|
+
* @returns
|
|
1237
|
+
*/
|
|
1238
|
+
static prepend(array, ...value) {
|
|
1239
|
+
return [...value, ...Array.isArray(array) ? array : []];
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Remove a value from an array by index and return it.
|
|
1243
|
+
* Returns a tuple: [newArray, removedValue]
|
|
1244
|
+
*/
|
|
1245
|
+
static pull(array, key) {
|
|
1246
|
+
if (!Array.isArray(array) || key < 0 || key >= array.length) return [array, void 0];
|
|
1247
|
+
const copy = array.slice();
|
|
1248
|
+
const [removed] = copy.splice(key, 1);
|
|
1249
|
+
return [copy, removed];
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Append values to an array (mutable)
|
|
1253
|
+
*/
|
|
1254
|
+
static push(array, ...values) {
|
|
1255
|
+
array.push(...values);
|
|
1256
|
+
return array;
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Pick one or more random elements from an array.
|
|
1260
|
+
*/
|
|
1261
|
+
static random(array, count = 1) {
|
|
1262
|
+
if (!Array.isArray(array) || array.length === 0) return void 0;
|
|
1263
|
+
const shuffled = Arr.shuffle(array);
|
|
1264
|
+
if (count === 1) return shuffled[0];
|
|
1265
|
+
return shuffled.slice(0, count);
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Returns array elements that do NOT satisfy the predicate
|
|
1269
|
+
*/
|
|
1270
|
+
static reject(array, predicate) {
|
|
1271
|
+
return array.filter((item) => !predicate(item));
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Pick keys from an array of objects or an object
|
|
1275
|
+
*/
|
|
1276
|
+
static select(obj, keys) {
|
|
1277
|
+
const result = {};
|
|
1278
|
+
keys.forEach((k) => {
|
|
1279
|
+
if (k in obj) result[k] = obj[k];
|
|
1280
|
+
});
|
|
1281
|
+
return result;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Returns the only element that passes a callback, throws if none or multiple
|
|
1285
|
+
*/
|
|
1286
|
+
static sole(array, predicate) {
|
|
1287
|
+
const filtered = array.filter(predicate);
|
|
1288
|
+
if (filtered.length === 0) throw new InvalidArgumentException("No element satisfies the condition.");
|
|
1289
|
+
if (filtered.length > 1) throw new InvalidArgumentException("Multiple elements satisfy the condition.");
|
|
1290
|
+
return filtered[0];
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Checks if at least one element satisfies the predicate
|
|
1294
|
+
*/
|
|
1295
|
+
static some(array, predicate) {
|
|
1296
|
+
return array.some(predicate);
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Randomly shuffle an array and return a new array.
|
|
1300
|
+
*/
|
|
1301
|
+
static shuffle(array) {
|
|
1302
|
+
if (!Array.isArray(array)) return [];
|
|
1303
|
+
const copy = array.slice();
|
|
1304
|
+
for (let i = copy.length - 1; i > 0; i--) {
|
|
1305
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1306
|
+
[copy[i], copy[j]] = [copy[j], copy[i]];
|
|
1307
|
+
}
|
|
1308
|
+
return copy;
|
|
1309
|
+
}
|
|
1310
|
+
/**
|
|
1311
|
+
* Sort an array ascending using optional comparator.
|
|
1312
|
+
*/
|
|
1313
|
+
static sort(array, comparator) {
|
|
1314
|
+
if (!Array.isArray(array)) return [];
|
|
1315
|
+
return array.slice().sort(comparator);
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Sort an array descending using optional comparator.
|
|
1319
|
+
*/
|
|
1320
|
+
static sortDesc(array, comparator) {
|
|
1321
|
+
return Arr.sort(array, comparator ? (a, b) => comparator(b, a) : void 0);
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Recursively sort arrays inside an array.
|
|
1325
|
+
*/
|
|
1326
|
+
static sortRecursive(array) {
|
|
1327
|
+
if (!Array.isArray(array)) return [];
|
|
1328
|
+
return array.map((item) => Array.isArray(item) ? Arr.sortRecursive(item) : item).sort();
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Recursively sort arrays inside an array descending.
|
|
1332
|
+
*/
|
|
1333
|
+
static sortRecursiveDesc(array) {
|
|
1334
|
+
if (!Array.isArray(array)) return [];
|
|
1335
|
+
return array.map((item) => Array.isArray(item) ? Arr.sortRecursiveDesc(item) : item).sort().reverse();
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Retrieve a value using dot notation
|
|
1339
|
+
* Throws if value is not a string
|
|
1340
|
+
*/
|
|
1341
|
+
static string(obj, path, defaultValue) {
|
|
1342
|
+
const val = data_get(obj, path, defaultValue);
|
|
1343
|
+
if (typeof val !== "string") throw new InvalidArgumentException(`Value at "${path}" is not a string.`);
|
|
1344
|
+
return val;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Return the first N elements of an array.
|
|
1348
|
+
*
|
|
1349
|
+
* @param array
|
|
1350
|
+
* @param count
|
|
1351
|
+
* @returns
|
|
1352
|
+
*/
|
|
1353
|
+
static take(array, count) {
|
|
1354
|
+
if (!Array.isArray(array)) return [];
|
|
1355
|
+
return array.slice(0, count);
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Filter an array based on a predicate function or key-value match.
|
|
1359
|
+
*/
|
|
1360
|
+
static where(array, predicate) {
|
|
1361
|
+
if (!Array.isArray(array)) return [];
|
|
1362
|
+
if (typeof predicate === "function") return array.filter(predicate);
|
|
1363
|
+
return array.filter((item) => Object.entries(predicate).every(([k, v]) => item[k] === v));
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* Filter an array of objects, keeping elements where the given key is not null/undefined.
|
|
1367
|
+
*/
|
|
1368
|
+
static whereNotNull(array, key) {
|
|
1369
|
+
if (!Array.isArray(array)) return [];
|
|
1370
|
+
return array.filter((item) => item[key] !== null && item[key] !== void 0);
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* If the given value is not an array and not null, wrap it in one.
|
|
1374
|
+
*
|
|
1375
|
+
* Non-array values become [value]; null/undefined becomes [].
|
|
1376
|
+
*
|
|
1377
|
+
* @param value
|
|
1378
|
+
* @returns
|
|
1379
|
+
*/
|
|
1380
|
+
static wrap(value) {
|
|
1381
|
+
if (value === null || value === void 0) return [];
|
|
1382
|
+
return Array.isArray(value) ? value : [value];
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Return the first element of an array, undefined if empty.
|
|
1386
|
+
*/
|
|
1387
|
+
static head(array) {
|
|
1388
|
+
return Array.isArray(array) && array.length ? array[0] : void 0;
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Splits an array into chunks of a specified size.
|
|
1392
|
+
*
|
|
1393
|
+
* @template T - Type of elements in the array
|
|
1394
|
+
* @param arr - The input array
|
|
1395
|
+
* @param size - Size of each chunk (default: 2)
|
|
1396
|
+
* @returns An array of chunks (arrays)
|
|
1397
|
+
*/
|
|
1398
|
+
static chunk = (arr, size = 2) => {
|
|
1399
|
+
if (size <= 0) throw new Error("Chunk size must be greater than 0");
|
|
1400
|
+
const chunks = [];
|
|
1401
|
+
for (let i = 0; i < arr.length; i += size) chunks.push(arr.slice(i, i + size));
|
|
1402
|
+
return chunks;
|
|
1403
|
+
};
|
|
1404
|
+
/**
|
|
1405
|
+
* Alternates between two arrays, creating a zipped result.
|
|
1406
|
+
*
|
|
1407
|
+
* @param a
|
|
1408
|
+
* @param b
|
|
1409
|
+
* @returns
|
|
1410
|
+
*/
|
|
1411
|
+
static alternate(a, b) {
|
|
1412
|
+
const result = [];
|
|
1413
|
+
const max = Math.max(a.length, b.length);
|
|
1414
|
+
for (let i = 0; i < max; i++) {
|
|
1415
|
+
if (i < a.length) result.push(a[i]);
|
|
1416
|
+
if (i < b.length) result.push(b[i]);
|
|
1417
|
+
}
|
|
1418
|
+
return result;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Combine arrays and sum their values element by element.
|
|
1422
|
+
*
|
|
1423
|
+
* @param arr
|
|
1424
|
+
* @returns
|
|
1425
|
+
*/
|
|
1426
|
+
static combine(...arr) {
|
|
1427
|
+
const maxLength = Math.max(...arr.map((a) => a.length));
|
|
1428
|
+
const result = new Array(maxLength).fill(0);
|
|
1429
|
+
for (let i = 0; i < maxLength; i++) for (const array of arr) result[i] += array[i] || 0;
|
|
1430
|
+
return result;
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Find the value associated with a given key.
|
|
1434
|
+
*
|
|
1435
|
+
* @param key
|
|
1436
|
+
* @param arr
|
|
1437
|
+
* @returns
|
|
1438
|
+
*/
|
|
1439
|
+
static find(key, arr) {
|
|
1440
|
+
return arr.find((item) => item === key) || null;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Check if array is empty.
|
|
1444
|
+
*
|
|
1445
|
+
* @param arr
|
|
1446
|
+
* @returns
|
|
1447
|
+
*/
|
|
1448
|
+
static isEmpty(arr) {
|
|
1449
|
+
if (arr.length === 0) return true;
|
|
1450
|
+
else return false;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Check if array is empty.
|
|
1454
|
+
*
|
|
1455
|
+
* @param arr
|
|
1456
|
+
* @returns
|
|
1457
|
+
*/
|
|
1458
|
+
static isNotEmpty(arr) {
|
|
1459
|
+
return arr.length > 0;
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Pop the element off the end of array.
|
|
1463
|
+
*
|
|
1464
|
+
* @param arr
|
|
1465
|
+
* @returns
|
|
1466
|
+
*/
|
|
1467
|
+
static pop(arr) {
|
|
1468
|
+
return arr.slice(0, -1);
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Create a new array in reverse order.
|
|
1472
|
+
*
|
|
1473
|
+
* @param arr
|
|
1474
|
+
* @returns
|
|
1475
|
+
*/
|
|
1476
|
+
static reverse(arr) {
|
|
1477
|
+
return [...arr].reverse();
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Return the first element of an array that satisfies the predicate,
|
|
1481
|
+
* or the first element if no predicate is provided, otherwise the defaultValue.
|
|
1482
|
+
*
|
|
1483
|
+
* Predicate can be true (boolean), a function or a value to match (strict equality).
|
|
1484
|
+
*
|
|
1485
|
+
* When predicate is true (boolean), the first element will be removed and a tuple will be returned [el, rest].
|
|
1486
|
+
*
|
|
1487
|
+
* @param array
|
|
1488
|
+
* @param predicate
|
|
1489
|
+
* @param defaultValue
|
|
1490
|
+
*
|
|
1491
|
+
* @alias Arr.first()
|
|
1492
|
+
* @returns
|
|
1493
|
+
*/
|
|
1494
|
+
static shift(array, predicate, defaultValue) {
|
|
1495
|
+
return Arr.first(array, predicate, defaultValue);
|
|
1496
|
+
}
|
|
1497
|
+
/**
|
|
1498
|
+
* Generates an array of sequential numbers.
|
|
1499
|
+
*
|
|
1500
|
+
* @param size - Number of elements in the range
|
|
1501
|
+
* @param startAt - Starting number (default: 0)
|
|
1502
|
+
* @returns An array of numbers from startAt to startAt + size - 1
|
|
1503
|
+
*/
|
|
1504
|
+
static range(size, startAt = 0) {
|
|
1505
|
+
if (size <= 0 || !Number.isFinite(size)) return [];
|
|
1506
|
+
return Array.from({ length: size }, (_, i) => startAt + i);
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
722
1509
|
|
|
723
1510
|
//#endregion
|
|
724
1511
|
//#region src/Helpers/Time.ts
|
|
@@ -5243,10 +6030,10 @@ function matchCase(value, comparison) {
|
|
|
5243
6030
|
*/
|
|
5244
6031
|
function loadHelpers(target = globalThis) {
|
|
5245
6032
|
const globalHelpers = {
|
|
5246
|
-
Arr
|
|
6033
|
+
Arr,
|
|
5247
6034
|
Crypto: Crypto_exports,
|
|
5248
6035
|
Number: Number_exports,
|
|
5249
|
-
Obj
|
|
6036
|
+
Obj,
|
|
5250
6037
|
Str,
|
|
5251
6038
|
DateTime,
|
|
5252
6039
|
DumpDie: DumpDie_exports,
|
|
@@ -5262,24 +6049,26 @@ function loadHelpers(target = globalThis) {
|
|
|
5262
6049
|
plural: Str.plural,
|
|
5263
6050
|
singular: Str.singular,
|
|
5264
6051
|
capitalize: Str.capitalize,
|
|
5265
|
-
collapse,
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
isEmpty,
|
|
5272
|
-
isNotEmpty,
|
|
5273
|
-
prepend,
|
|
5274
|
-
range,
|
|
5275
|
-
flatten,
|
|
6052
|
+
collapse: Arr.collapse,
|
|
6053
|
+
forget: Arr.forget,
|
|
6054
|
+
first: Arr.first,
|
|
6055
|
+
last: Arr.last,
|
|
6056
|
+
prepend: Arr.prepend,
|
|
6057
|
+
flatten: Arr.flatten,
|
|
5276
6058
|
dot,
|
|
6059
|
+
undot,
|
|
5277
6060
|
extractProperties,
|
|
5278
6061
|
getValue,
|
|
5279
6062
|
modObj,
|
|
5280
6063
|
safeDot,
|
|
5281
6064
|
setNested,
|
|
6065
|
+
toCssClasses,
|
|
5282
6066
|
slugifyKeys,
|
|
6067
|
+
toCssStyles,
|
|
6068
|
+
data_get,
|
|
6069
|
+
data_set,
|
|
6070
|
+
data_fill,
|
|
6071
|
+
data_forget,
|
|
5283
6072
|
uuid,
|
|
5284
6073
|
random,
|
|
5285
6074
|
randomSecure,
|
|
@@ -5361,6 +6150,13 @@ function cleanHelpers(target = globalThis) {
|
|
|
5361
6150
|
"safeDot",
|
|
5362
6151
|
"setNested",
|
|
5363
6152
|
"slugifyKeys",
|
|
6153
|
+
"toCssClasses",
|
|
6154
|
+
"undot",
|
|
6155
|
+
"toCssStyles",
|
|
6156
|
+
"data_get",
|
|
6157
|
+
"data_set",
|
|
6158
|
+
"data_fill",
|
|
6159
|
+
"data_forget",
|
|
5364
6160
|
"Crypto",
|
|
5365
6161
|
"uuid",
|
|
5366
6162
|
"random",
|
|
@@ -5398,12 +6194,7 @@ function cleanHelpers(target = globalThis) {
|
|
|
5398
6194
|
}
|
|
5399
6195
|
|
|
5400
6196
|
//#endregion
|
|
5401
|
-
|
|
5402
|
-
enumerable: true,
|
|
5403
|
-
get: function () {
|
|
5404
|
-
return Arr_exports;
|
|
5405
|
-
}
|
|
5406
|
-
});
|
|
6197
|
+
exports.Arr = Arr;
|
|
5407
6198
|
Object.defineProperty(exports, 'Crypto', {
|
|
5408
6199
|
enumerable: true,
|
|
5409
6200
|
get: function () {
|
|
@@ -5418,6 +6209,7 @@ Object.defineProperty(exports, 'DumpDie', {
|
|
|
5418
6209
|
}
|
|
5419
6210
|
});
|
|
5420
6211
|
exports.HtmlString = HtmlString;
|
|
6212
|
+
exports.InvalidArgumentException = InvalidArgumentException;
|
|
5421
6213
|
exports.Mode = Mode;
|
|
5422
6214
|
Object.defineProperty(exports, 'Number', {
|
|
5423
6215
|
enumerable: true,
|
|
@@ -5431,55 +6223,45 @@ Object.defineProperty(exports, 'Obj', {
|
|
|
5431
6223
|
return Obj_exports;
|
|
5432
6224
|
}
|
|
5433
6225
|
});
|
|
6226
|
+
exports.RuntimeException = RuntimeException;
|
|
5434
6227
|
exports.Str = Str;
|
|
5435
6228
|
exports.Stringable = Stringable;
|
|
5436
6229
|
exports.abbreviate = abbreviate;
|
|
5437
|
-
exports.alternate = alternate;
|
|
5438
6230
|
exports.base64Decode = base64Decode;
|
|
5439
6231
|
exports.base64Encode = base64Encode;
|
|
5440
6232
|
exports.caesarCipher = caesarCipher;
|
|
5441
6233
|
exports.checksum = checksum;
|
|
5442
|
-
exports.chunk = chunk;
|
|
5443
6234
|
exports.cleanHelpers = cleanHelpers;
|
|
5444
|
-
exports.
|
|
5445
|
-
exports.
|
|
6235
|
+
exports.data_fill = data_fill;
|
|
6236
|
+
exports.data_forget = data_forget;
|
|
6237
|
+
exports.data_get = data_get;
|
|
6238
|
+
exports.data_set = data_set;
|
|
5446
6239
|
exports.dd = dd;
|
|
5447
6240
|
exports.dot = dot;
|
|
5448
6241
|
exports.dump = dump;
|
|
5449
6242
|
exports.extractProperties = extractProperties;
|
|
5450
|
-
exports.find = find;
|
|
5451
|
-
exports.first = first;
|
|
5452
|
-
exports.flatten = flatten;
|
|
5453
|
-
exports.forget = forget;
|
|
5454
6243
|
exports.format = format;
|
|
5455
6244
|
exports.getValue = getValue;
|
|
5456
6245
|
exports.hash = hash;
|
|
5457
6246
|
exports.hmac = hmac;
|
|
5458
6247
|
exports.humanize = humanize;
|
|
5459
|
-
exports.isEmpty = isEmpty;
|
|
5460
|
-
exports.isNotEmpty = isNotEmpty;
|
|
5461
|
-
exports.last = last;
|
|
5462
6248
|
exports.loadHelpers = loadHelpers;
|
|
5463
6249
|
exports.modObj = modObj;
|
|
5464
|
-
exports.pop = pop;
|
|
5465
|
-
exports.prepend = prepend;
|
|
5466
6250
|
exports.random = random;
|
|
5467
6251
|
exports.randomColor = randomColor;
|
|
5468
6252
|
exports.randomPassword = randomPassword;
|
|
5469
6253
|
exports.randomSecure = randomSecure;
|
|
5470
|
-
exports.range = range;
|
|
5471
|
-
exports.reverse = reverse;
|
|
5472
6254
|
exports.safeDot = safeDot;
|
|
5473
6255
|
exports.secureToken = secureToken;
|
|
5474
6256
|
exports.setNested = setNested;
|
|
5475
|
-
exports.shift = shift;
|
|
5476
6257
|
exports.slugifyKeys = slugifyKeys;
|
|
5477
6258
|
exports.str = str;
|
|
5478
|
-
exports.take = take;
|
|
5479
6259
|
exports.toBytes = toBytes;
|
|
6260
|
+
exports.toCssClasses = toCssClasses;
|
|
6261
|
+
exports.toCssStyles = toCssStyles;
|
|
5480
6262
|
exports.toHumanTime = toHumanTime;
|
|
6263
|
+
exports.undot = undot;
|
|
5481
6264
|
exports.uuid = uuid;
|
|
5482
6265
|
exports.verifyChecksum = verifyChecksum;
|
|
5483
|
-
exports.wrap = wrap;
|
|
5484
6266
|
exports.xor = xor;
|
|
5485
6267
|
//# sourceMappingURL=index.cjs.map
|