toolbox-x 1.0.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.
Files changed (190) hide show
  1. package/LICENSE +201 -0
  2. package/dist/Color-B3mgF9Dh.d.cts +486 -0
  3. package/dist/Color-D38Xrw65.d.mts +486 -0
  4. package/dist/Stylog-Df7eq3-j.d.cts +519 -0
  5. package/dist/Stylog-jvlLcMQq.d.mts +519 -0
  6. package/dist/array-DvW0zIu6.d.mts +130 -0
  7. package/dist/array-rUnEVisO.d.cts +130 -0
  8. package/dist/basics-D_eSv0cu.cjs +132 -0
  9. package/dist/basics-Dp_aEK81.mjs +115 -0
  10. package/dist/basics-WEYWlnRO.d.cts +95 -0
  11. package/dist/basics-uBSfkBEI.d.mts +95 -0
  12. package/dist/case-BWIt8Ash.mjs +449 -0
  13. package/dist/case-C-S-b5YP.d.cts +327 -0
  14. package/dist/case-CS8Ii3A7.cjs +526 -0
  15. package/dist/case-CybASFPD.d.mts +327 -0
  16. package/dist/change-case.cjs +32 -0
  17. package/dist/change-case.d.cts +18 -0
  18. package/dist/change-case.d.mts +18 -0
  19. package/dist/change-case.mjs +19 -0
  20. package/dist/colors.cjs +574 -0
  21. package/dist/colors.d.cts +355 -0
  22. package/dist/colors.d.mts +355 -0
  23. package/dist/colors.mjs +547 -0
  24. package/dist/constants-2gAw23_7.mjs +144 -0
  25. package/dist/constants-B34K0QPi.d.cts +21 -0
  26. package/dist/constants-BIBDKY1u.cjs +924 -0
  27. package/dist/constants-BWT-810U.cjs +158 -0
  28. package/dist/constants-BwbHnXlM.mjs +662 -0
  29. package/dist/constants-BxN9l5el.cjs +74 -0
  30. package/dist/constants-CLS_bgKD.d.mts +847 -0
  31. package/dist/constants-D73iFu8g.mjs +171 -0
  32. package/dist/constants-DAfRxaa8.mjs +62 -0
  33. package/dist/constants-DQYeCjlx.cjs +207 -0
  34. package/dist/constants-Deeie-iH.d.mts +21 -0
  35. package/dist/constants-DpTG9RP6.d.mts +29 -0
  36. package/dist/constants-DqwnkJ_d.cjs +740 -0
  37. package/dist/constants-DvRUY_FY.cjs +150 -0
  38. package/dist/constants-VcRtQu0K.d.cts +29 -0
  39. package/dist/constants-X5hm1UtB.mjs +912 -0
  40. package/dist/constants-eNd-iYsV.mjs +134 -0
  41. package/dist/constants-qm8FafmD.d.cts +847 -0
  42. package/dist/constants.cjs +415 -0
  43. package/dist/constants.d.cts +184 -0
  44. package/dist/constants.d.mts +184 -0
  45. package/dist/constants.mjs +378 -0
  46. package/dist/convert-BOCgUv2D.cjs +252 -0
  47. package/dist/convert-Bn4jFomQ.mjs +169 -0
  48. package/dist/convert-BrzlG-m_.cjs +475 -0
  49. package/dist/convert-DhaUoPVU.mjs +368 -0
  50. package/dist/converter-1P90_RcP.d.mts +402 -0
  51. package/dist/converter-CmkcAppi.d.cts +402 -0
  52. package/dist/converter.cjs +780 -0
  53. package/dist/converter.d.cts +29 -0
  54. package/dist/converter.d.mts +29 -0
  55. package/dist/converter.mjs +771 -0
  56. package/dist/countries-CIpmtEzV.cjs +1469 -0
  57. package/dist/countries-Cy0xiqS3.mjs +1463 -0
  58. package/dist/css-colors-Bx947Ng3.d.cts +179 -0
  59. package/dist/css-colors-CXCDqQbG.cjs +186 -0
  60. package/dist/css-colors-CXTp1vvy.d.mts +179 -0
  61. package/dist/css-colors-DfUW3nTR.mjs +180 -0
  62. package/dist/date.cjs +332 -0
  63. package/dist/date.d.cts +213 -0
  64. package/dist/date.d.mts +213 -0
  65. package/dist/date.mjs +298 -0
  66. package/dist/dom.cjs +461 -0
  67. package/dist/dom.d.cts +228 -0
  68. package/dist/dom.d.mts +228 -0
  69. package/dist/dom.mjs +429 -0
  70. package/dist/form-BMFVGUrN.d.mts +118 -0
  71. package/dist/form-DRFbryvK.d.cts +118 -0
  72. package/dist/guards-3kaUX66g.mjs +157 -0
  73. package/dist/guards-C8gkvIHb.cjs +240 -0
  74. package/dist/guards-DdyU4h4o.mjs +110 -0
  75. package/dist/guards-Efhp1mNy.cjs +151 -0
  76. package/dist/guards.cjs +172 -0
  77. package/dist/guards.d.cts +399 -0
  78. package/dist/guards.d.mts +399 -0
  79. package/dist/guards.mjs +75 -0
  80. package/dist/hash-B6JPEyAz.d.mts +131 -0
  81. package/dist/hash-NTpeKYB_.d.cts +131 -0
  82. package/dist/hash.cjs +2126 -0
  83. package/dist/hash.d.cts +1239 -0
  84. package/dist/hash.d.mts +1239 -0
  85. package/dist/hash.mjs +2095 -0
  86. package/dist/http-status-BAZdtr7-.d.mts +65 -0
  87. package/dist/http-status-U_3MtoGb.d.cts +65 -0
  88. package/dist/http-status.cjs +173 -0
  89. package/dist/http-status.d.cts +142 -0
  90. package/dist/http-status.d.mts +142 -0
  91. package/dist/http-status.mjs +171 -0
  92. package/dist/index.cjs +2551 -0
  93. package/dist/index.d.cts +1493 -0
  94. package/dist/index.d.mts +1493 -0
  95. package/dist/index.mjs +2357 -0
  96. package/dist/object-B0TV3eHx.d.mts +8052 -0
  97. package/dist/object-Blq0Amdv.d.cts +8052 -0
  98. package/dist/objectify-CDs0Fbr1.mjs +417 -0
  99. package/dist/objectify-DIJ-OBmo.cjs +524 -0
  100. package/dist/paginator.cjs +245 -0
  101. package/dist/paginator.d.cts +144 -0
  102. package/dist/paginator.d.mts +144 -0
  103. package/dist/paginator.mjs +243 -0
  104. package/dist/parse-2ubxXZRp.cjs +211 -0
  105. package/dist/parse-N7g942uy.mjs +164 -0
  106. package/dist/pluralizer-BjMIc6uT.d.mts +42 -0
  107. package/dist/pluralizer-Cb6ZmrDl.d.cts +42 -0
  108. package/dist/pluralizer.cjs +678 -0
  109. package/dist/pluralizer.d.cts +152 -0
  110. package/dist/pluralizer.d.mts +152 -0
  111. package/dist/pluralizer.mjs +676 -0
  112. package/dist/primitives-B26uZolQ.cjs +228 -0
  113. package/dist/primitives-KsFUp3kQ.mjs +144 -0
  114. package/dist/specials-D48_IZbd.d.mts +108 -0
  115. package/dist/specials-DzLr1ZgU.cjs +477 -0
  116. package/dist/specials-LVONlKbQ.d.cts +108 -0
  117. package/dist/specials-uhDuRg8H.mjs +292 -0
  118. package/dist/string-CBAbxaG1.d.mts +258 -0
  119. package/dist/string-CsNsm_65.d.cts +258 -0
  120. package/dist/stylog.cjs +621 -0
  121. package/dist/stylog.d.cts +49 -0
  122. package/dist/stylog.d.mts +49 -0
  123. package/dist/stylog.mjs +614 -0
  124. package/dist/timezone-B2OYK6Fh.mjs +5589 -0
  125. package/dist/timezone-Beh9IGpw.cjs +5625 -0
  126. package/dist/types/array.cjs +16 -0
  127. package/dist/types/array.d.cts +18 -0
  128. package/dist/types/array.d.mts +18 -0
  129. package/dist/types/array.mjs +17 -0
  130. package/dist/types/colors.cjs +16 -0
  131. package/dist/types/colors.d.cts +18 -0
  132. package/dist/types/colors.d.mts +18 -0
  133. package/dist/types/colors.mjs +17 -0
  134. package/dist/types/converter.cjs +16 -0
  135. package/dist/types/converter.d.cts +18 -0
  136. package/dist/types/converter.d.mts +18 -0
  137. package/dist/types/converter.mjs +17 -0
  138. package/dist/types/form.cjs +16 -0
  139. package/dist/types/form.d.cts +18 -0
  140. package/dist/types/form.d.mts +18 -0
  141. package/dist/types/form.mjs +17 -0
  142. package/dist/types/hash.cjs +16 -0
  143. package/dist/types/hash.d.cts +18 -0
  144. package/dist/types/hash.d.mts +18 -0
  145. package/dist/types/hash.mjs +17 -0
  146. package/dist/types/http-status.cjs +16 -0
  147. package/dist/types/http-status.d.cts +18 -0
  148. package/dist/types/http-status.d.mts +18 -0
  149. package/dist/types/http-status.mjs +17 -0
  150. package/dist/types/index.cjs +16 -0
  151. package/dist/types/index.d.cts +18 -0
  152. package/dist/types/index.d.mts +18 -0
  153. package/dist/types/index.mjs +17 -0
  154. package/dist/types/number.cjs +16 -0
  155. package/dist/types/number.d.cts +18 -0
  156. package/dist/types/number.d.mts +18 -0
  157. package/dist/types/number.mjs +17 -0
  158. package/dist/types/object.cjs +16 -0
  159. package/dist/types/object.d.cts +18 -0
  160. package/dist/types/object.d.mts +18 -0
  161. package/dist/types/object.mjs +17 -0
  162. package/dist/types/pluralizer.cjs +16 -0
  163. package/dist/types/pluralizer.d.cts +18 -0
  164. package/dist/types/pluralizer.d.mts +18 -0
  165. package/dist/types/pluralizer.mjs +17 -0
  166. package/dist/types/string.cjs +16 -0
  167. package/dist/types/string.d.cts +18 -0
  168. package/dist/types/string.d.mts +18 -0
  169. package/dist/types/string.mjs +17 -0
  170. package/dist/types/stylog.cjs +16 -0
  171. package/dist/types/stylog.d.cts +18 -0
  172. package/dist/types/stylog.d.mts +18 -0
  173. package/dist/types/stylog.mjs +17 -0
  174. package/dist/types/utils.cjs +16 -0
  175. package/dist/types/utils.d.cts +18 -0
  176. package/dist/types/utils.d.mts +18 -0
  177. package/dist/types/utils.mjs +17 -0
  178. package/dist/types/verbalizer.cjs +16 -0
  179. package/dist/types/verbalizer.d.cts +30 -0
  180. package/dist/types/verbalizer.d.mts +30 -0
  181. package/dist/types/verbalizer.mjs +17 -0
  182. package/dist/utilities-CLUmdQeV.cjs +140 -0
  183. package/dist/utilities-m5yFKqLd.mjs +105 -0
  184. package/dist/utils-ClW9LA6f.mjs +449 -0
  185. package/dist/utils-DLFRgXUC.cjs +568 -0
  186. package/dist/verbalizer.cjs +998 -0
  187. package/dist/verbalizer.d.cts +148 -0
  188. package/dist/verbalizer.d.mts +148 -0
  189. package/dist/verbalizer.mjs +996 -0
  190. package/package.json +249 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2357 @@
1
+ /**
2
+ * Copyright 2026 - present Nazmul Hassan
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { a as isNonEmptyString, c as isNumber, d as isString, i as isInteger, m as isUndefined } from "./primitives-KsFUp3kQ.mjs";
18
+ import { n as convertStringCase, t as capitalizeString } from "./case-BWIt8Ash.mjs";
19
+ import { c as ORDINAL_UNDER_TEEN, d as TENS, f as THOUSANDS, l as PREFIX_MULTIPLIERS, o as ONES, s as ORDINAL_TO_CARDINAL, t as BN_DIGITS, u as TEENS } from "./constants-BwbHnXlM.mjs";
20
+ import { t as COUNTRIES } from "./countries-Cy0xiqS3.mjs";
21
+ import { T as isObject, b as isFunction, d as isNumericString, j as isValidArray, w as isNotEmptyObject } from "./specials-uhDuRg8H.mjs";
22
+ import { a as normalizeNumber, i as getRandomFloat, n as formatCurrency, o as roundToNearest, r as getOrdinal, t as clampNumber } from "./utilities-m5yFKqLd.mjs";
23
+ import { a as flattenObjectKeyValue, c as parseJsonToObject, i as flattenObjectDotNotation, l as parseObjectValues, n as extractUpdatedAndNewFields, o as mergeAndFlattenObjects, r as extractUpdatedFields, s as mergeObjects, t as extractNewFields, u as sanitizeData } from "./objectify-CDs0Fbr1.mjs";
24
+ import { _ as naturalSort, a as deepParsePrimitives, b as _resolveNestedKey, c as getInstanceGetterNames, d as getStaticMethodNames, f as isDeepEqual, g as throttleAction, h as stripJsonEdgeGarbage, i as debounceAction, l as getInstanceMethodNames, m as stableStringify, n as countInstanceMethods, o as definePrototypeMethod, p as parseJSON, r as countStaticMethods, s as getClassDetails, t as convertArrayToString, u as getStaticGetterNames, v as sortAnArray, y as _getNumericProp } from "./utils-ClW9LA6f.mjs";
25
+ import { n as trimString, r as truncateString, t as generateRandomID } from "./basics-Dp_aEK81.mjs";
26
+ import { a as normalizeString, c as slugifyString, i as maskString, l as areInvalidNumbers, n as extractURLs, o as replaceAllInString, p as isOdd, r as formatUnitWithPlural, s as reverseString, t as extractEmails, u as isEven } from "./convert-Bn4jFomQ.mjs";
27
+
28
+ //#region src/string/anagram.ts
29
+ /** `WeakMap` to cache user provided dictionary array */
30
+ const DICT_CACHE = /* @__PURE__ */ new WeakMap();
31
+ /** Get cached dictionary `Set` */
32
+ function _getDictSet(dict) {
33
+ if (DICT_CACHE.has(dict)) return DICT_CACHE.get(dict);
34
+ const dictSet = new Set(dict.map((w) => w.toLowerCase()));
35
+ DICT_CACHE.set(dict, dictSet);
36
+ return dictSet;
37
+ }
38
+ /**
39
+ * * Generates unique anagrams of a given word. Optionally looks up valid anagrams inside a provided dictionary.
40
+ *
41
+ * @param word The word from which to generate anagrams. Converted to lowercase internally.
42
+ *
43
+ * @param options Controls the output limit and optional dictionary lookup.
44
+ *
45
+ * @returns A list of unique, lowercase anagrams. The original word (lowercased) is always included as the first element.
46
+ *
47
+ * @remarks
48
+ * - When a dictionary is provided, only anagrams found inside that dictionary are returned.
49
+ * - The dictionary is cached internally (with converting to lowercase) using a `WeakMap`, allowing garbage collection of unused inputs.
50
+ * - Repeated letters are handled efficiently using a per-level set to avoid duplicate permutations.
51
+ *
52
+ * @example
53
+ * generateAnagrams("east", { limit: 10 });
54
+ *
55
+ * @example
56
+ * generateAnagrams("tone", {
57
+ * dictionary: ["tone", "note", "one"],
58
+ * limit: "all"
59
+ * });
60
+ */
61
+ function generateAnagrams(word, options) {
62
+ if (!isNonEmptyString(word)) return [];
63
+ if (word?.length === 1) return [word?.toLowerCase()];
64
+ const { limit = 100, dictionary = false } = options || {};
65
+ const outSet = /* @__PURE__ */ new Set();
66
+ const dictSet = isValidArray(dictionary) ? _getDictSet(dictionary) : void 0;
67
+ /** Helper function to generate permutations. */
68
+ const _permute = (current, remaining) => {
69
+ if (!remaining.length) {
70
+ if (!dictSet || dictSet.has(current)) outSet.add(current);
71
+ return;
72
+ }
73
+ const usedSet = /* @__PURE__ */ new Set();
74
+ for (let i = 0; i < remaining.length; i++) {
75
+ const char = remaining[i];
76
+ if (usedSet.has(char)) continue;
77
+ usedSet.add(char);
78
+ if (limit !== "all" && outSet.size >= limit) return;
79
+ _permute(current + char, remaining.slice(0, i) + remaining.slice(i + 1));
80
+ }
81
+ };
82
+ _permute("", word.toLowerCase());
83
+ return [...outSet];
84
+ }
85
+
86
+ //#endregion
87
+ //#region src/string/utilities.ts
88
+ /**
89
+ * * Extracts all numbers from a string as array of numbers.
90
+ * @param input - The string to extract numbers from.
91
+ * @returns An array of numbers found in the string.
92
+ */
93
+ const extractNumbersFromString = (input) => {
94
+ return (input.match(/\d+/g) || [])?.map(Number);
95
+ };
96
+ /**
97
+ * * Computes the Levenshtein distance between two strings (space optimized).
98
+ * @param str1 - First string to compare.
99
+ * @param str2 - Second string to compare.
100
+ * @returns The Levenshtein distance between the two strings.
101
+ *
102
+ * @remarks
103
+ * - The Levenshtein distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other.
104
+ * - This implementation uses only O(min(len(a), len(b))) space by keeping only the current and previous rows of the distance matrix.
105
+ *
106
+ * @example
107
+ * const distance = getLevenshteinDistance('kitten', 'sitting');
108
+ * console.log(distance); // Output: 3
109
+ */
110
+ const getLevenshteinDistance = (str1, str2) => {
111
+ if (str1 === str2) return 0;
112
+ const lenA = str1?.length;
113
+ const lenB = str2?.length;
114
+ if (lenA < lenB) return getLevenshteinDistance(str2, str1);
115
+ let prev = Array.from({ length: lenB + 1 }, (_, j) => j);
116
+ let curr = new Array(lenB + 1);
117
+ for (let i = 1; i <= lenA; i++) {
118
+ curr[0] = i;
119
+ for (let j = 1; j <= lenB; j++) curr[j] = str1[i - 1] === str2[j - 1] ? prev[j - 1] : 1 + Math.min(prev[j], curr[j - 1], prev[j - 1]);
120
+ [prev, curr] = [curr, prev];
121
+ }
122
+ return prev[lenB];
123
+ };
124
+ /**
125
+ * * Counts the number of words in a string, supporting multiple languages and scripts.
126
+ *
127
+ * @param text - The input string to count words from.
128
+ * @returns Number of words (Unicode-aware).
129
+ */
130
+ function countWords(text) {
131
+ return (text?.match(/\p{L}[\p{L}\p{M}\p{Pd}'’]*|\p{N}+/gu) || [])?.length;
132
+ }
133
+
134
+ //#endregion
135
+ //#region src/string/helpers.ts
136
+ /**
137
+ * Calculates the similarity between two strings using the Levenshtein edit distance.
138
+ *
139
+ * @param str1 The first string to compare.
140
+ * @param str2 The second string to compare.
141
+ * @returns A score between `0` and `1`, where `1` means identical and `0` means completely different.
142
+ */
143
+ function _calculateSimilarity(str1, str2) {
144
+ if (str1 === str2) return 1;
145
+ const maxLen = Math.max(str1.length, str2.length);
146
+ if (maxLen === 0) return 1;
147
+ return 1 - getLevenshteinDistance(str1, str2) / maxLen;
148
+ }
149
+ /**
150
+ * Builds an LCS (Longest Common Subsequence) table for two strings at the character level.
151
+ *
152
+ * @param original The original string.
153
+ * @param modified The modified string.
154
+ * @returns A 2D matrix where each cell `[i][j]` holds the LCS length of `original[0..i-1]` and `modified[0..j-1]`.
155
+ */
156
+ function _buildCharLcsTable(original, modified) {
157
+ const origLen = original.length;
158
+ const modLen = modified.length;
159
+ const lcs = Array(origLen + 1).fill(null).map(() => Array(modLen + 1).fill(0));
160
+ for (let i = 1; i <= origLen; i++) for (let j = 1; j <= modLen; j++) if (original[i - 1] === modified[j - 1]) lcs[i][j] = lcs[i - 1][j - 1] + 1;
161
+ else lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
162
+ return lcs;
163
+ }
164
+ /**
165
+ * Backtracks through an LCS table to extract matched character indices in both strings.
166
+ *
167
+ * @param original The original string.
168
+ * @param modified The modified string.
169
+ * @param lcs The precomputed LCS table from {@link buildCharLcsTable}.
170
+ * @returns A tuple `[origMatched, modMatched]` — sets of matched character indices for the original and modified strings respectively.
171
+ */
172
+ function _getLcsIndices(original, modified, lcs) {
173
+ const origLen = original.length;
174
+ const modLen = modified.length;
175
+ const origMatched = /* @__PURE__ */ new Set();
176
+ const modMatched = /* @__PURE__ */ new Set();
177
+ let i = origLen;
178
+ let j = modLen;
179
+ while (i > 0 && j > 0) if (original[i - 1] === modified[j - 1]) {
180
+ origMatched.add(i - 1);
181
+ modMatched.add(j - 1);
182
+ i--;
183
+ j--;
184
+ } else if (lcs[i - 1][j] > lcs[i][j - 1]) i--;
185
+ else j--;
186
+ return [origMatched, modMatched];
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/string/diff.ts
191
+ /**
192
+ * * Computes a line-based text diff between two strings using the Longest Common Subsequence (LCS) algorithm.
193
+ *
194
+ * @remarks
195
+ * - Lines are classified as `added`, `removed`, `modified`, or `unchanged`.
196
+ * - Detects and pairs similar `removed` and `added` lines as `modified` when their similarity exceeds the threshold.
197
+ *
198
+ * @param originalText The original (before) text.
199
+ * @param modifiedText The modified (after) text.
200
+ * @returns A {@link DiffResult} with the list of diff lines and summary statistics.
201
+ *
202
+ * @example
203
+ * const result = computeTextDiff('hello\nworld', 'hello\nearth');
204
+ * // result.stats → { linesAdded: 1, linesRemoved: 1, linesChanged: 0, linesUnchanged: 1 }
205
+ *
206
+ * @example
207
+ * const result = computeTextDiff('foo\nbar', 'foo\nbaz\nqux');
208
+ * result.stats.linesAdded; // 1
209
+ * result.stats.linesChanged; // 1
210
+ */
211
+ function computeTextDiff(originalText, modifiedText) {
212
+ const originalLines = originalText.split("\n");
213
+ const modifiedLines = modifiedText.split("\n");
214
+ const originalLen = originalLines.length;
215
+ const modifiedLen = modifiedLines.length;
216
+ const lcs = Array(originalLen + 1).fill(null).map(() => Array(modifiedLen + 1).fill(0));
217
+ for (let i = 1; i <= originalLen; i++) for (let j = 1; j <= modifiedLen; j++) if (originalLines[i - 1] === modifiedLines[j - 1]) lcs[i][j] = lcs[i - 1][j - 1] + 1;
218
+ else lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
219
+ const diffLines = [];
220
+ let i = originalLen;
221
+ let j = modifiedLen;
222
+ while (i > 0 || j > 0) if (i > 0 && j > 0 && originalLines[i - 1] === modifiedLines[j - 1]) {
223
+ diffLines.unshift({
224
+ type: "unchanged",
225
+ original: originalLines[i - 1],
226
+ modified: modifiedLines[j - 1],
227
+ originalLineNum: i,
228
+ modifiedLineNum: j
229
+ });
230
+ i--;
231
+ j--;
232
+ } else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
233
+ diffLines.unshift({
234
+ type: "added",
235
+ modified: modifiedLines[j - 1],
236
+ modifiedLineNum: j
237
+ });
238
+ j--;
239
+ } else if (i > 0) {
240
+ diffLines.unshift({
241
+ type: "removed",
242
+ original: originalLines[i - 1],
243
+ originalLineNum: i
244
+ });
245
+ i--;
246
+ }
247
+ const processedLines = [];
248
+ const usedIndices = /* @__PURE__ */ new Set();
249
+ const SIMILARITY_THRESHOLD = .6;
250
+ for (let idx = 0; idx < diffLines.length; idx++) {
251
+ if (usedIndices.has(idx)) continue;
252
+ const line = diffLines[idx];
253
+ if (line.type === "removed") {
254
+ for (let jdx = idx + 1; jdx < diffLines.length; jdx++) {
255
+ if (usedIndices.has(jdx)) continue;
256
+ const nextLine = diffLines[jdx];
257
+ if (nextLine.type === "added") {
258
+ if (_calculateSimilarity(line.original || "", nextLine.modified || "") >= SIMILARITY_THRESHOLD) {
259
+ processedLines.push({
260
+ type: "modified",
261
+ original: line.original,
262
+ modified: nextLine.modified,
263
+ originalLineNum: line.originalLineNum,
264
+ modifiedLineNum: nextLine.modifiedLineNum
265
+ });
266
+ usedIndices.add(idx);
267
+ usedIndices.add(jdx);
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ if (!usedIndices.has(idx)) {
273
+ processedLines.push(line);
274
+ usedIndices.add(idx);
275
+ }
276
+ } else if (line.type !== "added" || !usedIndices.has(idx)) {
277
+ if (line.type !== "added" || !usedIndices.has(idx)) {
278
+ processedLines.push(line);
279
+ usedIndices.add(idx);
280
+ }
281
+ }
282
+ }
283
+ let linesAdded = 0;
284
+ let linesRemoved = 0;
285
+ let linesChanged = 0;
286
+ let linesUnchanged = 0;
287
+ for (const line of processedLines) if (line.type === "added") linesAdded++;
288
+ else if (line.type === "removed") linesRemoved++;
289
+ else if (line.type === "modified") linesChanged++;
290
+ else if (line.type === "unchanged") linesUnchanged++;
291
+ return {
292
+ lines: processedLines,
293
+ stats: {
294
+ linesAdded,
295
+ linesRemoved,
296
+ linesChanged,
297
+ linesUnchanged
298
+ }
299
+ };
300
+ }
301
+ /**
302
+ * * Highlights character-level differences between two strings using the LCS algorithm.
303
+ *
304
+ * @remarks
305
+ * - The function returns two arrays of characters for the original and modified strings.
306
+ * - Each character in both strings is annotated with a `highlighted` flag indicating whether it differs from the other string.
307
+ *
308
+ * @param original The original string to compare from.
309
+ * @param modified The modified string to compare to.
310
+ * @returns A {@link CharDiffResult} with annotated character arrays for both strings.
311
+ *
312
+ * @example
313
+ * const diff = getCharacterDifferences('cat', 'car');
314
+ * diff.original; // [{ text: 'c', highlighted: false }, { text: 'a', highlighted: false }, { text: 't', highlighted: true }]
315
+ * diff.modified; // [{ text: 'c', highlighted: false }, { text: 'a', highlighted: false }, { text: 'r', highlighted: true }]
316
+ *
317
+ * @example
318
+ * // When one string is empty, all characters in the other are highlighted
319
+ * getCharacterDifferences('', 'hi');
320
+ * // { original: [], modified: [{ text: 'h', highlighted: true }, { text: 'i', highlighted: true }] }
321
+ *
322
+ * @example
323
+ * const diff = getCharacterDifferences('hello world', 'hello earth');
324
+ * // Characters unique to each string will have highlighted: true
325
+ */
326
+ function getCharacterDifferences(original, modified) {
327
+ const result = {
328
+ original: [],
329
+ modified: []
330
+ };
331
+ if (!original && !modified) return result;
332
+ if (!original) return {
333
+ original: [],
334
+ modified: modified.split("").map((text) => ({
335
+ text,
336
+ highlighted: true
337
+ }))
338
+ };
339
+ if (!modified) return {
340
+ original: original.split("").map((text) => ({
341
+ text,
342
+ highlighted: true
343
+ })),
344
+ modified: []
345
+ };
346
+ const [origMatched, modMatched] = _getLcsIndices(original, modified, _buildCharLcsTable(original, modified));
347
+ for (let i = 0; i < original.length; i++) result.original.push({
348
+ text: original[i],
349
+ highlighted: !origMatched.has(i)
350
+ });
351
+ for (let j = 0; j < modified.length; j++) result.modified.push({
352
+ text: modified[j],
353
+ highlighted: !modMatched.has(j)
354
+ });
355
+ return result;
356
+ }
357
+
358
+ //#endregion
359
+ //#region src/number/helpers.ts
360
+ /**
361
+ * Apply multiples of a number if there is any.
362
+ * @param array Array of numbers to apply the condition on.
363
+ * @param multiples The multiples of which number.
364
+ * @returns Array of multiples of the desired number
365
+ */
366
+ const _applyMultiples = (array, multiples) => {
367
+ if (!multiples) return array;
368
+ return array?.filter((n) => n % multiples === 0);
369
+ };
370
+ /**
371
+ * - Converts a number less than 1000 to words.
372
+ * @param num - The number to convert (less than 1000).
373
+ * @param isLast - Whether this is the last group (thousands, millions, etc.).
374
+ * @returns Numbers less than 1000 in words.
375
+ */
376
+ function _convertLessThanThousand(num, isLast) {
377
+ if (num < 10) return ONES[num];
378
+ if (num < 20) return TEENS[num - 10];
379
+ let result = TENS[Math.floor(num / 10)];
380
+ const remainder = num % 10;
381
+ if (remainder > 0) result += `-${ONES[remainder]}`;
382
+ if (num >= 100) {
383
+ const hundredsPart = `${ONES[Math.floor(num / 100)]} hundred`;
384
+ return num % 100 === 0 ? hundredsPart : `${hundredsPart} ${isLast ? "and" : ""} ${_convertLessThanThousand(num % 100, false)}`;
385
+ }
386
+ return result;
387
+ }
388
+ /**
389
+ * * Calculate the HCF (Highest Common Factor) of two numbers using the Euclidean algorithm.
390
+ *
391
+ * @param a - First number.
392
+ * @param b - Second number.
393
+ * @returns The HCF of the two numbers.
394
+ */
395
+ const _find2NumbersHCF = (a, b) => {
396
+ let x = Math.abs(a);
397
+ let y = Math.abs(b);
398
+ while (y !== 0) {
399
+ const temp = y;
400
+ y = x % y;
401
+ x = temp;
402
+ }
403
+ return x;
404
+ };
405
+ /**
406
+ * * Calculate the LCM (Least Common Multiple) of two numbers using the Euclidean algorithm.
407
+ *
408
+ * @param a - First number.
409
+ * @param b - Second number.
410
+ * @returns The LCM of the two numbers.
411
+ */
412
+ const _find2NumbersLCM = (a, b) => {
413
+ const x = Math.abs(a);
414
+ const y = Math.abs(b);
415
+ return x * y / _find2NumbersHCF(x, y);
416
+ };
417
+
418
+ //#endregion
419
+ //#region src/number/basics.ts
420
+ /**
421
+ * * Utility to generate a random number between a given range.
422
+ * * If no options are provided, it will generate a random number between `0` and `100` (inclusive).
423
+ * * If `min` is greater than `max`, it will swap the values and generate a random number.
424
+ *
425
+ * @param options - Options for configuring random number generator.
426
+ * @returns Random number.
427
+ */
428
+ const getRandomNumber = (options) => {
429
+ const { min = 0, max = 100, includeMin = true, includeMax = true } = options || {};
430
+ let minimum = min, maximum = max;
431
+ if (min > max) {
432
+ [minimum, maximum] = [max, min];
433
+ return getRandomNumber({
434
+ min: minimum,
435
+ max: maximum,
436
+ includeMin,
437
+ includeMax
438
+ });
439
+ }
440
+ if (min === max) return min;
441
+ if (includeMin && includeMax) return Math.floor(Math.random() * (max - min + 1)) + min;
442
+ if (!includeMin && !includeMax) return Math.floor(Math.random() * (max - min - 1)) + min + 1;
443
+ if (includeMin && !includeMax) return Math.floor(Math.random() * (max - min)) + min;
444
+ if (!includeMin && includeMax) return Math.floor(Math.random() * (max - min)) + min + 1;
445
+ return 0;
446
+ };
447
+ /**
448
+ * * Utility to round a number to given decimal places.
449
+ *
450
+ * @param input - Number or `stringified` number to round.
451
+ * @param options - Options for rounding behavior, including decimal places and return type.
452
+ * @returns Converted number as `number` (default) or `string` (if `isString` is `true`).
453
+ */
454
+ const convertToDecimal = (input, options) => {
455
+ const { decimalPlaces = 2, isString = false } = options || {};
456
+ const parsed = normalizeNumber(input);
457
+ if (isUndefined(parsed)) throw new TypeError(`Invalid numeric input!`, { cause: `${input} cannot be converted to a number.` });
458
+ return isString ? parsed.toFixed(decimalPlaces) : Number(parsed.toFixed(decimalPlaces));
459
+ };
460
+ /**
461
+ * * Calculates the HCF/GCD of multiple numbers.
462
+ *
463
+ * @param numbers - List of numbers to find the HCF/GCD for.
464
+ * @returns The HCF/GCD of all the provided numbers.
465
+ */
466
+ const calculateHCF = (...numbers) => {
467
+ const converted = numbers?.map(Number);
468
+ if (converted?.length === 0) return 0;
469
+ let hcf = converted[0];
470
+ for (let i = 1; i < converted?.length; i++) hcf = _find2NumbersHCF(hcf, converted[i]);
471
+ return hcf;
472
+ };
473
+ /**
474
+ * * Calculates the LCM/LCD of multiple numbers.
475
+ *
476
+ * @param numbers - List of numbers to find the LCM/LCD for.
477
+ * @returns The LCM/LCD of all the provided numbers.
478
+ */
479
+ const calculateLCM = (...numbers) => {
480
+ const converted = numbers?.map(Number);
481
+ if (converted?.length === 0) return 0;
482
+ let lcm = converted[0];
483
+ for (let i = 1; i < converted?.length; i++) lcm = _find2NumbersLCM(lcm, converted[i]);
484
+ return lcm;
485
+ };
486
+ /**
487
+ * * Computes the factorial of a non-negative numeric value (integer).
488
+ *
489
+ * @param int - A numeric input value (integer) whose factorial should be calculated.
490
+ *
491
+ * @returns The factorial result as a number if valid, otherwise `undefined`.
492
+ *
493
+ * @example
494
+ * factorial(5); // → 120
495
+ * factorial(0); // → 1
496
+ * factorial(-3); // → undefined
497
+ * factorial(undefined); // → undefined
498
+ * factorial(5.5); // → undefined
499
+ *
500
+ * @remarks
501
+ * - Factorial of `0` and `1` is `1`.
502
+ * - Uses recursive approach internally.
503
+ * - Input is normalized via `normalizeNumber` before computation.
504
+ * - May return large values quickly due to factorial growth rate.
505
+ * - Returns `undefined` if the input is negative, not numeric, non-integer, or `undefined`.
506
+ */
507
+ function factorial(int) {
508
+ const num = normalizeNumber(int);
509
+ if (!isNumber(num) || num < 0 || !Number.isInteger(num)) return;
510
+ else if (num === 0 || num === 1) return 1;
511
+ else return num * (factorial(num - 1) ?? 1);
512
+ }
513
+ /**
514
+ * * Efficiently computes all positive integer factors (divisors) of a number.
515
+ *
516
+ * @param int - Numeric value to find factors for. Non-integer or negative values return an empty array.
517
+ *
518
+ * @returns An array of positive factors in ascending order.
519
+ *
520
+ * @example
521
+ * getFactors(12); // → [1, 2, 3, 4, 6, 12]
522
+ * getFactors(7); // → [1, 7]
523
+ * getFactors(-4); // → []
524
+ * getFactors(undefined); // → []
525
+ *
526
+ * @remarks
527
+ * - Uses the square root method for better performance (`O(√n)`).
528
+ * - Returns an empty array for invalid, negative, or non-integer input.
529
+ */
530
+ function getFactors(int) {
531
+ const num = normalizeNumber(int);
532
+ if (!isNumber(num) || num <= 0 || !Number.isInteger(num)) return [];
533
+ if (num === 1) return [1];
534
+ const factors = new Set([1, num]);
535
+ const sqrt = Math.floor(Math.sqrt(num));
536
+ for (let i = 2; i <= sqrt; i++) if (num % i === 0) {
537
+ factors.add(i);
538
+ factors.add(num / i);
539
+ }
540
+ return [...factors].sort((a, b) => a - b);
541
+ }
542
+ /**
543
+ * * Sums up all digits of a number.
544
+ *
545
+ * @param num The input number.
546
+ * @returns The sum of its digits.
547
+ */
548
+ function sumDigits(num) {
549
+ return Math.abs(Number(num)).toString().split("").reduce((sum, digit) => sum + Number(digit), 0);
550
+ }
551
+ /**
552
+ * * Sums up numbers.
553
+ *
554
+ * @param numbers The input numbers.
555
+ * @returns The sum of the numbers.
556
+ */
557
+ function sumNumbers(...numbers) {
558
+ return numbers?.map((num) => Number(num))?.reduce((sum, number) => sum + number, 0);
559
+ }
560
+ /**
561
+ * * Reverses a number (e.g., `123` → `321`).
562
+ *
563
+ * @param num The number to reverse.
564
+ * @returns The reversed number.
565
+ */
566
+ function reverseNumber(num) {
567
+ const reversed = parseInt(Math.abs(Number(num)).toString().split("").reverse().join(""), 10);
568
+ return Number(num) < 0 ? -reversed : reversed;
569
+ }
570
+ /**
571
+ * * Calculates the average of a set of numbers.
572
+ *
573
+ * @param numbers - A list of numbers for which to calculate the average.
574
+ * @returns The average of the provided numbers. Returns `NaN` if no numbers are valid.
575
+ */
576
+ function getAverage(...numbers) {
577
+ let sum = 0;
578
+ let count = 0;
579
+ for (const n of numbers) {
580
+ const num = Number(n);
581
+ if (isNumber(num)) {
582
+ sum += num;
583
+ count++;
584
+ }
585
+ }
586
+ return count === 0 ? NaN : Math.round(sum / count * 1e3) / 1e3;
587
+ }
588
+ /**
589
+ * * Rounds a number to a specified number of decimal places.
590
+ *
591
+ * @param number - The number to round.
592
+ * @param roundTo - The number of decimal places to round to (default is `2`).
593
+ * - If `roundTo` is negative, the number is rounded to the left of the decimal point (e.g., `-1` rounds to the nearest 10, `-2` to nearest 100 etc.).
594
+ * @returns The rounded number, either in float or integer (if a whole number).
595
+ *
596
+ * @example
597
+ * roundNumber(1234.56, -2); // 1200
598
+ * roundNumber(1234.56, 1); // 1234.6
599
+ */
600
+ function roundNumber(number, roundTo = 2) {
601
+ const factor = Math.pow(10, roundTo);
602
+ const num = isNumber(number) ? number : Number(number);
603
+ return Math.round(num * factor) / factor;
604
+ }
605
+
606
+ //#endregion
607
+ //#region src/number/Currency.ts
608
+ /**
609
+ * * A utility class for handling currency operations like formatting and conversion.
610
+ *
611
+ * - Supports formatting based on locale and currency code.
612
+ * - Converts between **fiat currencies supported by `api.frankfurter.app`**.
613
+ * - Automatically caches conversion rates to reduce redundant API calls.
614
+ * - Intended for use with numeric inputs (number or numeric string).
615
+ */
616
+ var Currency = class Currency {
617
+ #amount;
618
+ #code;
619
+ /**
620
+ * * The formatted currency string (e.g., `$1,000.00`).
621
+ *
622
+ * - Generated using the `en-US` locale during construction.
623
+ * - This is a display-friendly version of the currency value.
624
+ * - For formatting with other locales, use the `format()` method.
625
+ */
626
+ currency;
627
+ /**
628
+ * Creates an instance of the Currency class.
629
+ *
630
+ * @param amount - The numeric amount of currency (e.g., `100`, `'99.99'`).
631
+ * @param code - The ISO 4217 currency code representing the currency (e.g., `'USD'`, `'EUR'`).
632
+ */
633
+ constructor(amount, code) {
634
+ this.#amount = Number(amount);
635
+ this.#code = code;
636
+ this.currency = this.format("en-US");
637
+ }
638
+ static #RATE_CACHE = /* @__PURE__ */ new Map();
639
+ /** * Clears cached rates that were fetched previously. */
640
+ static clearRateCache() {
641
+ Currency.#RATE_CACHE.clear();
642
+ }
643
+ /**
644
+ * @instance Formats the stored amount as a localized currency string.
645
+ *
646
+ * @param locale - Optional. A BCP 47 locale string (e.g., `'de-DE'`, `'en-US'`). Defaults to `'en-US'` if not provided.
647
+ * @param code - Optional. An ISO 4217 currency code (e.g., `'USD'`, `'EUR'`) used solely for formatting purposes.
648
+ * _This does not alter the internal currency code set during instantiation._
649
+ * @returns A string representing the formatted currency value according to the specified locale and currency code.
650
+ */
651
+ format(locale, code) {
652
+ return formatCurrency(this.#amount, code ?? this.#code, locale);
653
+ }
654
+ /**
655
+ * @instance Converts the current currency amount to a target currency using real-time exchange rates.
656
+ *
657
+ * - Uses {@link https://api.frankfurter.app/latest api.frankfurter.app} to fetch live exchange rates.
658
+ * - Supports **only the following fiat currencies**:
659
+ * `AUD`, `BGN`, `BRL`, `CAD`, `CHF`, `CNY`, `CZK`, `DKK`, `EUR`, `GBP`, `HKD`, `HUF`, `IDR`, `ILS`, `INR`, `ISK`, `JPY`,
660
+ * `KRW`, `MXN`, `MYR`, `NOK`, `NZD`, `PHP`, `PLN`, `RON`, `SEK`, `SGD`, `THB`, `TRY`, `USD`, `ZAR`.
661
+ * - Uses cached rates unless `forceRefresh` is set to `true`.
662
+ * - If API fails or currency not supported, falls back to `fallbackRate` if provided.
663
+ * - Use {@link convertSync} method to convert to other currencies using custom exchange rate.
664
+ *
665
+ * @param to - The target currency code (must be one of the supported ones, e.g., `'EUR'`, `'USD'`).
666
+ * @param options - Optional settings:
667
+ * - `fallbackRate`: A manual exchange rate to use if the API call fails or currency is not supported.
668
+ * - `forceRefresh`: If true, ignores cached rates and fetches fresh data.
669
+ * @returns A new `Currency` instance with the converted amount in the target currency.
670
+ * @throws Will throw error if the API call fails and no `fallbackRate` is provided.
671
+ *
672
+ * @example
673
+ * await new Currency(100, 'USD').convert('EUR');
674
+ */
675
+ async convert(to, options) {
676
+ const key = `${this.#code}->${to}`;
677
+ if (!options?.forceRefresh && Currency.#RATE_CACHE.has(key)) {
678
+ const cachedRate = Currency.#RATE_CACHE.get(key);
679
+ return new Currency(this.#amount * cachedRate, to);
680
+ }
681
+ try {
682
+ const rate = await this.#fetchFromFrankfurter(to);
683
+ Currency.#RATE_CACHE.set(key, rate);
684
+ return new Currency(this.#amount * rate, to);
685
+ } catch (error) {
686
+ if (options?.fallbackRate != null) {
687
+ console.warn(`Currency conversion failed (${this.#code} → ${to}): ${error?.message}. Using fallback rate...`);
688
+ return new Currency(this.#amount * options.fallbackRate, to);
689
+ } else throw new Error(`Currency conversion failed (${this.#code} → ${to}): ${error?.message}`);
690
+ }
691
+ }
692
+ /**
693
+ * @instance Converts the current currency amount to a target currency using either a cached rate or a manual exchange rate.
694
+ *
695
+ * - This method is **synchronous** and does **not perform any network requests**.
696
+ * - If a cached rate exists for the currency pair, it is used.
697
+ * - If no cached rate is found, `rate` is used as a manual exchange rate.
698
+ * - If neither are available, the original instance is returned unchanged.
699
+ *
700
+ * @param to - The target currency code to convert to.
701
+ * @param rate - A manual exchange rate to use if no cached rate is available.
702
+ * @returns A new `Currency` instance with the converted amount, or the original instance if no rate is available.
703
+ *
704
+ * @example
705
+ * const usd = new Currency(100, 'USD');
706
+ * const eur = usd.convertSync('EUR', 0.92);
707
+ *
708
+ * console.log(eur.currency); // €92.00
709
+ */
710
+ convertSync(to, rate) {
711
+ const key = `${this.#code}->${to}`;
712
+ const cachedRate = Currency.#RATE_CACHE.get(key);
713
+ if (cachedRate) return new Currency(this.#amount * cachedRate, to);
714
+ else if (rate) return new Currency(this.#amount * rate, to);
715
+ else return this;
716
+ }
717
+ /**
718
+ * @private Attempts to fetch rate from frankfurter.app
719
+ * @param to - Target currency code
720
+ * @returns Exchange rate (multiplier)
721
+ */
722
+ async #fetchFromFrankfurter(to) {
723
+ const url = `https://api.frankfurter.app/latest?amount=1&from=${this.#code}`;
724
+ try {
725
+ const res = await fetch(url, { redirect: "error" });
726
+ if (!res.ok) throw new Error(`FrankFurter Error: ${res.status}. "${res.statusText}"`);
727
+ const data = await res.json();
728
+ if (!data.rates?.[to]) throw new Error(`Currency "${to}" is not found in FrankFurter Database!`);
729
+ return data.rates[to];
730
+ } catch (error) {
731
+ throw new Error(error?.message || `Failed to fetch data from FrankFurter API`);
732
+ }
733
+ }
734
+ };
735
+
736
+ //#endregion
737
+ //#region src/number/Unit.ts
738
+ /**
739
+ * @class Class to handle conversions between various types of units.
740
+ *
741
+ * Includes static methods for:
742
+ * - Length: meters, feet, kilometers, miles
743
+ * - Mass: kilograms, pounds, grams, ounces
744
+ * - Temperature: Celsius, Fahrenheit, Kelvin
745
+ * - Volume: liters, gallons
746
+ * - Area: square meters, square feet
747
+ * - Speed: km/h, mph
748
+ * - Time: hours, minutes, seconds, days
749
+ * - Digital Storage: kilobytes, megabytes, gigabytes
750
+ * - Energy: joules, calories
751
+ * - Pressure: atm, pascals
752
+ * - Frequency: Hz, kHz
753
+ *
754
+ * @remarks For more robust unit conversion, please use the `Converter` function which provides category-specific conversion classes.
755
+ */
756
+ var Unit = class Unit {
757
+ #value;
758
+ #unit;
759
+ /**
760
+ * * Creates an instance of the Unit class.
761
+ * @param value The numeric value to work with.
762
+ * @param unit The unit type of the value (e.g., 'kg', 'm', 'kb').
763
+ */
764
+ constructor(value, unit) {
765
+ this.#value = value;
766
+ this.#unit = unit;
767
+ }
768
+ /**
769
+ * @instance Returns the original value with unit (if passed in the constructor).
770
+ * @returns A string in the format "value unit".
771
+ */
772
+ toString() {
773
+ return ` ${this.#value} ${this.#unit ?? ""}`.trim();
774
+ }
775
+ /**
776
+ * @instance Converts using scientific prefixes (e.g., kB to MB, mg to g).
777
+ *
778
+ * @param fromPrefix The SI prefix of the source unit.
779
+ * @param toPrefix The SI prefix of the target unit.
780
+ * @returns The converted numeric value.
781
+ */
782
+ convertByPrefix(fromPrefix, toPrefix) {
783
+ return Unit.convertByPrefix(this.#value, fromPrefix, toPrefix);
784
+ }
785
+ /**
786
+ * @instance Converts from prefixed unit string to another (e.g., kB to MB, mg to g).
787
+ *
788
+ * @param from Prefixed unit string (e.g., 'kB', 'mg').
789
+ * @param to Target prefixed unit string (e.g., 'MB', 'g').
790
+ * @returns The converted numeric value.
791
+ */
792
+ convertFromTo(from, to) {
793
+ return Unit.convertFromTo(this.#value, from, to);
794
+ }
795
+ /**
796
+ * @instance Converts the value using a static method name from the `Unit` class.
797
+ *
798
+ * - **N.B.** *Provides IntelliSense and type safety for method selection.*
799
+ *
800
+ * @param methodName - A static `Unit` method that accepts a number and returns a number.
801
+ * @returns The converted numeric value.
802
+ */
803
+ convert(methodName) {
804
+ const method = Unit[methodName];
805
+ if (typeof method !== "function") throw new Error(`Method ${methodName} is not a valid method!`);
806
+ return method(this.#value);
807
+ }
808
+ /**
809
+ * @static Converts a value using scientific prefixes (e.g., kB to MB, mg to g).
810
+ *
811
+ * @param value The value to convert.
812
+ * @param fromPrefix The SI prefix of the source unit.
813
+ * @param toPrefix The SI prefix of the target unit.
814
+ * @returns The converted numeric value.
815
+ */
816
+ static convertByPrefix(value, fromPrefix, toPrefix) {
817
+ const fromMultiplier = PREFIX_MULTIPLIERS[fromPrefix];
818
+ const toMultiplier = PREFIX_MULTIPLIERS[toPrefix];
819
+ return value * fromMultiplier / toMultiplier;
820
+ }
821
+ /**
822
+ * @static Converts from prefixed unit string to another (e.g., kB to MB, mg to g).
823
+ *
824
+ * @param value The numeric value.
825
+ * @param from Prefixed unit string (e.g., 'kB', 'mg').
826
+ * @param to Target prefixed unit string (e.g., 'MB', 'g').
827
+ * @returns The converted numeric value.
828
+ */
829
+ static convertFromTo(value, from, to) {
830
+ const extractPrefix = (str) => {
831
+ const match = str.match(/^(da|[yzafpnμumcdhkMGTPEZY]?)(.+)$/);
832
+ if (!match) throw new Error(`Invalid unit format: ${str}`);
833
+ return [match[1], match[2]];
834
+ };
835
+ const [fromPrefix, fromUnit] = extractPrefix(from);
836
+ const [toPrefix, toUnit] = extractPrefix(to);
837
+ if (fromUnit !== toUnit) throw new Error(`Mismatched units: ${fromUnit} vs ${toUnit}`);
838
+ return Unit.convertByPrefix(value, fromPrefix, toPrefix);
839
+ }
840
+ /** Converts meters to feet. */
841
+ static metersToFeet(m) {
842
+ return m * 3.28084;
843
+ }
844
+ /** Converts feet to meters. */
845
+ static feetToMeters(ft) {
846
+ return ft / 3.28084;
847
+ }
848
+ /** Converts kilometers to miles. */
849
+ static kmToMiles(km) {
850
+ return km * .621371;
851
+ }
852
+ /** Converts miles to kilometers. */
853
+ static milesToKm(mi) {
854
+ return mi / .621371;
855
+ }
856
+ /** Converts kilograms to pounds. */
857
+ static kgToLbs(kg) {
858
+ return kg * 2.20462;
859
+ }
860
+ /** Converts pounds to kilograms. */
861
+ static lbsToKg(lbs) {
862
+ return lbs / 2.20462;
863
+ }
864
+ /** Converts grams to ounces. */
865
+ static gramsToOunces(g) {
866
+ return g * .035274;
867
+ }
868
+ /** Converts ounces to grams. */
869
+ static ouncesToGrams(oz) {
870
+ return oz / .035274;
871
+ }
872
+ /** Converts Celsius to Fahrenheit. */
873
+ static celsiusToFahrenheit(c) {
874
+ return c * 9 / 5 + 32;
875
+ }
876
+ /** Converts Fahrenheit to Celsius. */
877
+ static fahrenheitToCelsius(f) {
878
+ return (f - 32) * 5 / 9;
879
+ }
880
+ /** Converts Celsius to Kelvin. */
881
+ static celsiusToKelvin(c) {
882
+ return c + 273.15;
883
+ }
884
+ /** Converts Kelvin to Celsius. */
885
+ static kelvinToCelsius(k) {
886
+ return k - 273.15;
887
+ }
888
+ /** Converts Fahrenheit to Kelvin. */
889
+ static fahrenheitToKelvin(f) {
890
+ return (f - 32) * 5 / 9 + 273.15;
891
+ }
892
+ /** Converts Kelvin to Fahrenheit. */
893
+ static kelvinToFahrenheit(k) {
894
+ return (k - 273.15) * 9 / 5 + 32;
895
+ }
896
+ /** Converts milliliters to liters. */
897
+ static mlToLiters(ml) {
898
+ return ml / 1e3;
899
+ }
900
+ /** Converts liters to milliliters. */
901
+ static litersToMl(l) {
902
+ return l * 1e3;
903
+ }
904
+ /** Converts gallons to milliliters. */
905
+ static gallonsToMl(gal) {
906
+ return gal * 3785.41;
907
+ }
908
+ /** Converts milliliters to gallons. */
909
+ static mlToGallons(ml) {
910
+ return ml / 3785.41;
911
+ }
912
+ /** Converts liters to gallons. */
913
+ static litersToGallons(l) {
914
+ return l * .264172;
915
+ }
916
+ /** Converts gallons to liters. */
917
+ static gallonsToLiters(gal) {
918
+ return gal / .264172;
919
+ }
920
+ /** Converts square meters to square feet. */
921
+ static sqmToSqft(sqm) {
922
+ return sqm * 10.7639;
923
+ }
924
+ /** Converts square feet to square meters. */
925
+ static sqftToSqm(sqft) {
926
+ return sqft / 10.7639;
927
+ }
928
+ /** Converts kilometers per hour to miles per hour. */
929
+ static kmphToMph(kmph) {
930
+ return kmph * .621371;
931
+ }
932
+ /** Converts miles per hour to kilometers per hour. */
933
+ static mphToKmph(mph) {
934
+ return mph / .621371;
935
+ }
936
+ /** Converts minutes to hours. */
937
+ static minutesToHours(min) {
938
+ return min / 60;
939
+ }
940
+ /** Converts seconds to minutes. */
941
+ static secondsToMinutes(sec) {
942
+ return sec / 60;
943
+ }
944
+ /** Converts hours to days. */
945
+ static hoursToDays(hr) {
946
+ return hr / 24;
947
+ }
948
+ /** Converts hours to minutes. */
949
+ static hoursToMinutes(h) {
950
+ return h * 60;
951
+ }
952
+ /** Converts minutes to seconds. */
953
+ static minutesToSeconds(m) {
954
+ return m * 60;
955
+ }
956
+ /** Converts days to hours. */
957
+ static daysToHours(d) {
958
+ return d * 24;
959
+ }
960
+ /** Converts megabytes to gigabytes. */
961
+ static mbToGb(mb) {
962
+ return mb / 1024;
963
+ }
964
+ /** Converts gigabytes to megabytes. */
965
+ static gbToMb(gb) {
966
+ return gb * 1024;
967
+ }
968
+ /** Converts kilobytes to megabytes. */
969
+ static kbToMb(kb) {
970
+ return kb / 1024;
971
+ }
972
+ /** Converts kilobytes to gigabytes. */
973
+ static kbToGb(kb) {
974
+ return kb / (1024 * 1024);
975
+ }
976
+ /** Converts gigabytes to kilobytes. */
977
+ static gbToKb(gb) {
978
+ return gb * 1024 * 1024;
979
+ }
980
+ /** Converts bytes to kilobytes. */
981
+ static bytesToKb(bytes) {
982
+ return bytes / 1024;
983
+ }
984
+ /** Converts kilobytes to bytes. */
985
+ static kbToBytes(kb) {
986
+ return kb * 1024;
987
+ }
988
+ /** Converts megabytes to kilobytes. */
989
+ static mbToKb(mb) {
990
+ return mb * 1024;
991
+ }
992
+ /** Converts gigabytes to terabytes. */
993
+ static gbToTb(gb) {
994
+ return gb / 1024;
995
+ }
996
+ /** Converts terabytes to gigabytes. */
997
+ static tbToGb(tb) {
998
+ return tb * 1024;
999
+ }
1000
+ /** Converts joules to calories. */
1001
+ static joulesToCalories(j) {
1002
+ return j * .239006;
1003
+ }
1004
+ /** Converts calories to joules. */
1005
+ static caloriesToJoules(cal) {
1006
+ return cal / .239006;
1007
+ }
1008
+ /** Converts calories to kilojoules. */
1009
+ static caloriesToKJoules(cal) {
1010
+ return cal / .239006 / 1e3;
1011
+ }
1012
+ /** Converts kilojoules to calories. */
1013
+ static kJoulesToCalories(kj) {
1014
+ return kj * 1e3 * .239006;
1015
+ }
1016
+ /** Converts atmospheres to pascals. */
1017
+ static atmToPascal(atm) {
1018
+ return atm * 101325;
1019
+ }
1020
+ /** Converts pascals to atmospheres. */
1021
+ static pascalToAtm(pa) {
1022
+ return pa / 101325;
1023
+ }
1024
+ /** Converts bar to pascals. */
1025
+ static barToPascal(bar) {
1026
+ return bar * 1e5;
1027
+ }
1028
+ /** Converts pascals to bar. */
1029
+ static pascalToBar(pa) {
1030
+ return pa / 1e5;
1031
+ }
1032
+ /** Converts hertz to kilohertz. */
1033
+ static hzToKHz(hz) {
1034
+ return hz / 1e3;
1035
+ }
1036
+ /** Converts kilohertz to hertz. */
1037
+ static kHzToHz(khz) {
1038
+ return khz * 1e3;
1039
+ }
1040
+ /** Converts hertz to megahertz. */
1041
+ static hzToMHz(hz) {
1042
+ return hz / 1e6;
1043
+ }
1044
+ /** Converts megahertz to hertz. */
1045
+ static mHzToHz(mhz) {
1046
+ return mhz * 1e6;
1047
+ }
1048
+ /** Converts kilohertz to megahertz. */
1049
+ static kHzToMHz(khz) {
1050
+ return khz / 1e3;
1051
+ }
1052
+ /** Converts megahertz to kilohertz. */
1053
+ static mHzToKHz(mhz) {
1054
+ return mhz * 1e3;
1055
+ }
1056
+ /** Converts centimeters to meters. */
1057
+ static cmToMeters(cm) {
1058
+ return cm / 100;
1059
+ }
1060
+ /** Converts meters to centimeters. */
1061
+ static metersToCm(m) {
1062
+ return m * 100;
1063
+ }
1064
+ /** Converts millimeters to meters. */
1065
+ static mmToMeters(mm) {
1066
+ return mm / 1e3;
1067
+ }
1068
+ /** Converts meters to millimeters. */
1069
+ static metersToMm(m) {
1070
+ return m * 1e3;
1071
+ }
1072
+ /** Converts square kilometers to square meters. */
1073
+ static sqkmToSqm(sqkm) {
1074
+ return sqkm * 1e6;
1075
+ }
1076
+ /** Converts square meters to square kilometers. */
1077
+ static sqmToSqkm(sqm) {
1078
+ return sqm / 1e6;
1079
+ }
1080
+ /** Converts square feet to square inches. */
1081
+ static sqftToSqin(sqft) {
1082
+ return sqft * 144;
1083
+ }
1084
+ /** Converts square inches to square feet. */
1085
+ static sqinToSqft(sqin) {
1086
+ return sqin / 144;
1087
+ }
1088
+ /** Converts watts to kilowatts. */
1089
+ static wattsToKw(w) {
1090
+ return w / 1e3;
1091
+ }
1092
+ /** Converts kilowatts to watts. */
1093
+ static kwToWatts(kw) {
1094
+ return kw * 1e3;
1095
+ }
1096
+ };
1097
+
1098
+ //#endregion
1099
+ //#region src/number/percent.ts
1100
+ /**
1101
+ * * Performs a percentage-related calculation based on the given mode and inputs.
1102
+ *
1103
+ * - `get-percent`: Calculates what percentage the `part` is of the `total`.
1104
+ * - `get-value`: Calculates the value from a given `percentage` of a `total`.
1105
+ * - `get-original`: Calculates the original value from a known `value` and `percentage`.
1106
+ * - `get-change-percent`: Percent increase/decrease from `oldValue` to `newValue`.
1107
+ * - `apply-percent-change`: Applies increase/decrease by `percentage` to `baseValue`.
1108
+ * - `get-percent-difference`: Absolute percent difference between two values.
1109
+ * - `inverse-percent`: What percent `total` is of `part`.
1110
+ *
1111
+ * @param options - The calculation mode and inputs required for the operation.
1112
+ * @returns The calculated number rounded to three decimal places, or `NaN` if input is invalid.
1113
+ */
1114
+ function calculatePercentage(options) {
1115
+ const { roundTo = 3 } = options;
1116
+ /**
1117
+ * - Rounds a number to the specified number of decimal places.
1118
+ *
1119
+ * @param num - The number to round.
1120
+ * @returns The rounded number.
1121
+ */
1122
+ const _roundNumber = (num) => {
1123
+ const factor = Math.pow(10, roundTo);
1124
+ return Math.round(num * factor) / factor;
1125
+ };
1126
+ switch (options?.mode) {
1127
+ case "get-percent": {
1128
+ const { part, total } = options;
1129
+ if (areInvalidNumbers(part, total) || total === 0) return NaN;
1130
+ return _roundNumber(part / total * 100);
1131
+ }
1132
+ case "get-value": {
1133
+ const { percentage, total } = options;
1134
+ if (areInvalidNumbers(percentage, total) || total === 0) return NaN;
1135
+ return _roundNumber(percentage / 100 * total);
1136
+ }
1137
+ case "get-original": {
1138
+ const { percentage, value } = options;
1139
+ if (areInvalidNumbers(percentage, value) || percentage === 0) return NaN;
1140
+ return _roundNumber(value / percentage * 100);
1141
+ }
1142
+ case "get-change-percent": {
1143
+ const { oldValue, newValue } = options;
1144
+ if (areInvalidNumbers(oldValue, newValue) || oldValue === 0) return NaN;
1145
+ return _roundNumber((newValue - oldValue) / oldValue * 100);
1146
+ }
1147
+ case "apply-percent-change": {
1148
+ const { baseValue, percentage } = options;
1149
+ if (areInvalidNumbers(baseValue, percentage)) return NaN;
1150
+ return _roundNumber(baseValue * (1 + percentage / 100));
1151
+ }
1152
+ case "get-percent-difference": {
1153
+ const { value1, value2 } = options;
1154
+ if (areInvalidNumbers(value1, value2)) return NaN;
1155
+ const avg = getAverage(value1, value2);
1156
+ if (avg === 0) return NaN;
1157
+ return _roundNumber(Math.abs(value1 - value2) / avg * 100);
1158
+ }
1159
+ case "inverse-percent": {
1160
+ const { part, total } = options;
1161
+ if (areInvalidNumbers(part, total) || part === 0) return NaN;
1162
+ return _roundNumber(total / part * 100);
1163
+ }
1164
+ default: return NaN;
1165
+ }
1166
+ }
1167
+
1168
+ //#endregion
1169
+ //#region src/number/fibonacci.ts
1170
+ /**
1171
+ * * Generates the first `limit` Fibonacci numbers.
1172
+ *
1173
+ * @param limit The number of Fibonacci numbers to generate.
1174
+ * @returns An array containing the first `limit` Fibonacci numbers.
1175
+ */
1176
+ function getFibonacciSeries(limit) {
1177
+ const cLimit = Number(limit);
1178
+ if (!Number.isFinite(cLimit) || cLimit <= 0) return [];
1179
+ if (cLimit === 1) return [0];
1180
+ const series = [0, 1];
1181
+ for (let i = 2; i < cLimit; i++) series.push(series[i - 1] + series[i - 2]);
1182
+ return series;
1183
+ }
1184
+ /**
1185
+ * * Generates the first `limit` Fibonacci numbers using recursion with memoization.
1186
+ *
1187
+ * @param limit - The number of Fibonacci numbers to generate.
1188
+ * @returns An array containing the first `limit` Fibonacci numbers.
1189
+ */
1190
+ function getFibonacciSeriesMemo(limit) {
1191
+ const cLimit = Number(limit);
1192
+ if (!Number.isFinite(cLimit) || cLimit <= 0) return [];
1193
+ if (cLimit === 1) return [0];
1194
+ const memo = new Map([[0, 0], [1, 1]]);
1195
+ const fib = (n) => {
1196
+ if (memo.has(n)) return memo.get(n);
1197
+ const val = fib(n - 1) + fib(n - 2);
1198
+ memo.set(n, val);
1199
+ return val;
1200
+ };
1201
+ return Array.from({ length: cLimit }, (_, i) => fib(i));
1202
+ }
1203
+ /**
1204
+ * * Generator function for Fibonacci sequence up to a given limit.
1205
+ *
1206
+ * @param limit - Number of Fibonacci numbers to generate.
1207
+ * @param onYield - Optional callback triggered on each yield with the current value and index.
1208
+ * @returns A generator yielding Fibonacci numbers one by one.
1209
+ */
1210
+ function* fibonacciGenerator(limit, onYield) {
1211
+ const cLimit = Number(limit);
1212
+ if (!Number.isFinite(cLimit) || cLimit < 0) return;
1213
+ let a = 0;
1214
+ let b = 1;
1215
+ for (let i = 0; i < cLimit; i++) {
1216
+ onYield?.(a, i);
1217
+ yield a;
1218
+ [a, b] = [b, a + b];
1219
+ }
1220
+ }
1221
+ /**
1222
+ * * Calculates the `n`th Fibonacci number using optimized space.
1223
+ *
1224
+ * @param index - The index (0-based) of the Fibonacci number.
1225
+ * @returns The index=`n`th Fibonacci number.
1226
+ */
1227
+ function getNthFibonacci(index) {
1228
+ const n = Number(index);
1229
+ if (!Number.isFinite(n) || n < 0) return NaN;
1230
+ if (n === 0) return 0;
1231
+ if (n === 1) return 1;
1232
+ let a = 0, b = 1;
1233
+ for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
1234
+ return b;
1235
+ }
1236
+
1237
+ //#endregion
1238
+ //#region src/number/convert.ts
1239
+ /**
1240
+ * * Converts a numeric value into its corresponding English word representation.
1241
+ * @warning ***Supports numeric values up to `10e19` or `10^20` (one hundred quintillion).***
1242
+ * @warning ***Decimal values are ignored; only the integer part is converted.***
1243
+ * @param number - The number to convert into words.
1244
+ * @returns The number converted in words.
1245
+ */
1246
+ function numberToWords(num) {
1247
+ let number = Math.trunc(Number(num));
1248
+ if (!Number.isFinite(number) || isNaN(number)) return "Invalid Number!";
1249
+ const isNegative = number < 0;
1250
+ if (number === 0) return "zero";
1251
+ number = Math.abs(number);
1252
+ let i = 0;
1253
+ let result = "";
1254
+ while (number > 0) {
1255
+ if (i >= THOUSANDS.length) return `Number exceeds supported range (max is 10e19 aka 10^20)`;
1256
+ if (number % 1e3 !== 0) {
1257
+ const isLastGroup = i === 0 && number % 100 < 100;
1258
+ result = `${_convertLessThanThousand(number % 1e3, isLastGroup)} ${THOUSANDS[i]} ${result}`;
1259
+ }
1260
+ number = Math.floor(number / 1e3);
1261
+ i++;
1262
+ }
1263
+ const finalResult = result.trim().replace(/\s+/g, " ");
1264
+ return isNegative ? `minus ${finalResult}` : finalResult;
1265
+ }
1266
+ /**
1267
+ * * Converts a number to an uppercase Roman numeral.
1268
+ * @param value - The number to convert. Number must be an integer and `between 1 and 3999`.
1269
+ * @returns The Roman numeral representation in uppercase.
1270
+ *
1271
+ * @example convertToRomanNumerals(29) // → "XXIX"
1272
+ */
1273
+ const convertToRomanNumerals = (value) => {
1274
+ let num = normalizeNumber(value);
1275
+ if (!isInteger(num) || num <= 0 || num >= 4e3) throw new RangeError("Value must be an integer and between 1 and 3999");
1276
+ const romanMap = [
1277
+ [1e3, "M"],
1278
+ [900, "CM"],
1279
+ [500, "D"],
1280
+ [400, "CD"],
1281
+ [100, "C"],
1282
+ [90, "XC"],
1283
+ [50, "L"],
1284
+ [40, "XL"],
1285
+ [10, "X"],
1286
+ [9, "IX"],
1287
+ [5, "V"],
1288
+ [4, "IV"],
1289
+ [1, "I"]
1290
+ ];
1291
+ let result = "";
1292
+ for (const [value, numeral] of romanMap) while (num >= value) {
1293
+ result += numeral;
1294
+ num -= value;
1295
+ }
1296
+ return result;
1297
+ };
1298
+ /**
1299
+ * * Converts a Roman numeral to its Arabic numeric representation.
1300
+ * @param roman - The Roman numeral to convert. Case-insensitive but must represent a valid Roman numeral (`I`–`MMMCMXCIX`) otherwise throws runtime error.
1301
+ * @returns The numeric (Arabic system) representation of the Roman numeral.
1302
+ *
1303
+ * @example
1304
+ * romanToInteger("XXIX") // → 29
1305
+ * romanToInteger("mmxxv") // → 2025
1306
+ */
1307
+ const romanToInteger = (roman) => {
1308
+ const romanMap = {
1309
+ I: 1,
1310
+ V: 5,
1311
+ X: 10,
1312
+ L: 50,
1313
+ C: 100,
1314
+ D: 500,
1315
+ M: 1e3
1316
+ };
1317
+ if (!isNonEmptyString(roman) || !roman?.trim()) throw new TypeError("Input must be a non-empty string");
1318
+ const upperRoman = roman?.toUpperCase()?.trim();
1319
+ let total = 0;
1320
+ let prevValue = 0;
1321
+ for (let i = upperRoman.length - 1; i >= 0; i--) {
1322
+ const char = upperRoman[i];
1323
+ const value = romanMap[char];
1324
+ if (!value) throw new Error(`Invalid Roman numeral character: '${char}'`);
1325
+ if (value < prevValue) total -= value;
1326
+ else {
1327
+ total += value;
1328
+ prevValue = value;
1329
+ }
1330
+ }
1331
+ if (total <= 0 || total >= 4e3) throw new RangeError("Resulting number must be between 1 and 3999");
1332
+ if (convertToRomanNumerals(total) !== upperRoman) throw new Error("Invalid or malformed Roman numeral!");
1333
+ return total;
1334
+ };
1335
+ /**
1336
+ * * Converts a number, numeric string, or cardinal word string into its ordinal word representation.
1337
+ *
1338
+ * @param number - A number (e.g. `42`), numeric string (e.g. `"42"`), or cardinal word (e.g. `"forty-two"`).
1339
+ * @returns The ordinal word form (always in lowercase) of the input.
1340
+ *
1341
+ * @example
1342
+ * numberToWordsOrdinal(1); // "first"
1343
+ * numberToWordsOrdinal("23"); // "twenty-third"
1344
+ * numberToWordsOrdinal("twenty-three"); // "twenty-third"
1345
+ */
1346
+ function numberToWordsOrdinal(number) {
1347
+ const TEEN_OR_HUNDRED = /(teen|hundred|thousand|(m|b|tr|quadr)illion)$/;
1348
+ const UNDER_TEEN = /(zero|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve)$/;
1349
+ const _fixUnderTeen = (cardinal) => {
1350
+ return ORDINAL_UNDER_TEEN[cardinal];
1351
+ };
1352
+ const wordNumber = isNumericString(number) || isNumber(number) ? numberToWords(number) : number?.trim()?.toLowerCase();
1353
+ if (TEEN_OR_HUNDRED.test(wordNumber)) return wordNumber + "th";
1354
+ else if (/y$/.test(wordNumber)) return wordNumber.replace(/y$/, "ieth");
1355
+ else if (UNDER_TEEN.test(wordNumber)) return wordNumber.replace(UNDER_TEEN, _fixUnderTeen);
1356
+ else return wordNumber;
1357
+ }
1358
+ /**
1359
+ * * Convert an English cardinal/ordinal word string into a number.
1360
+ *
1361
+ * - Accepts hyphenated words, "and", ordinals (first, second, etc.), negatives, and large scales (thousand, million etc.).
1362
+ *
1363
+ * @example
1364
+ * wordsToNumber('forty-two') // 42
1365
+ * wordsToNumber('one hundred and seven') // 107
1366
+ * wordsToNumber('two thousand three hundred') // 2300
1367
+ * wordsToNumber('twenty-first') // 21
1368
+ * wordsToNumber('negative five') // -5
1369
+ *
1370
+ * @param word - A human readable number (cardinal or ordinal) in words
1371
+ * @returns Numeric value of the word or NaN if cannot parse
1372
+ *
1373
+ * @remarks
1374
+ * **NOTE** - *For very large numbers (e.g. more than quintillion) results may not always be correct.*
1375
+ */
1376
+ function wordsToNumber(word) {
1377
+ if (!isNonEmptyString(word)) return NaN;
1378
+ const trimmed = word.trim();
1379
+ if (/^[+-]?\d{1,3}(,\d{3})*(\.\d+)?$/.test(trimmed)) return Number(trimmed.replace(/,/g, ""));
1380
+ if (/^[+-]?\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
1381
+ let input = trimmed.toLowerCase();
1382
+ let negative = false;
1383
+ input = input.replace(/^\s*(minus|negative)\s+/, () => {
1384
+ negative = true;
1385
+ return "";
1386
+ });
1387
+ input = input.replace(/-/g, " ").replace(/\s+and\s+/g, " ").replace(/\s+/g, " ").trim();
1388
+ if (!input) return NaN;
1389
+ const onesMap = new Map(ONES.map((w, i) => [w === "" ? "zero" : w, i]));
1390
+ const teensMap = new Map(TEENS.map((w, i) => [w, 10 + i]));
1391
+ const tensMap = new Map(TENS.map((w, i) => [w, i * 10]));
1392
+ const scalesMap = new Map(THOUSANDS.map((w, i) => [w, i === 0 ? 1 : 1e3 ** i]));
1393
+ const PROTECTED_TOKENS = new Set([
1394
+ ...ONES,
1395
+ ...TEENS,
1396
+ ...TENS,
1397
+ ...THOUSANDS,
1398
+ "hundred",
1399
+ "zero"
1400
+ ].filter(Boolean));
1401
+ const tokens = input.split(" ").map((token) => {
1402
+ if (ORDINAL_TO_CARDINAL[token]) return ORDINAL_TO_CARDINAL[token];
1403
+ if (PROTECTED_TOKENS.has(token)) return token;
1404
+ return token.replace(/(teenth|tieth|ieth|th|st|nd|rd)$/i, "");
1405
+ }).filter(Boolean);
1406
+ if (tokens.length === 0) return NaN;
1407
+ let total = 0;
1408
+ let currentNumber = 0;
1409
+ let hasInvalidToken = false;
1410
+ for (const token of tokens) {
1411
+ if (hasInvalidToken) break;
1412
+ if (onesMap.has(token)) {
1413
+ currentNumber += onesMap.get(token);
1414
+ continue;
1415
+ }
1416
+ if (teensMap.has(token)) {
1417
+ currentNumber += teensMap.get(token);
1418
+ continue;
1419
+ }
1420
+ if (tensMap.has(token)) {
1421
+ currentNumber += tensMap.get(token);
1422
+ continue;
1423
+ }
1424
+ if (token === "hundred") {
1425
+ currentNumber = (currentNumber || 1) * 100;
1426
+ continue;
1427
+ }
1428
+ if (scalesMap.has(token)) {
1429
+ const scale = scalesMap.get(token);
1430
+ if (scale > 1) {
1431
+ total += (currentNumber || 1) * scale;
1432
+ currentNumber = 0;
1433
+ continue;
1434
+ }
1435
+ }
1436
+ if (/^\d+$/.test(token)) {
1437
+ currentNumber += Number(token);
1438
+ continue;
1439
+ }
1440
+ hasInvalidToken = true;
1441
+ }
1442
+ if (hasInvalidToken) return NaN;
1443
+ total += currentNumber;
1444
+ return negative ? -total : total;
1445
+ }
1446
+ /**
1447
+ * * Converts Bangla (Arabic system) digits to Latin (Arabic system) digits.
1448
+ *
1449
+ * @remarks
1450
+ * - Behavior depends on the `forceNumber` flag:
1451
+ * - When `forceNumber` is `true`, always returns a `number` (strips non-digit characters).
1452
+ * - Returns `NaN` if the input is non empty string or does not include any numeric string.
1453
+ * - When `forceNumber` is `false`, always returns a string, including non-digit characters.
1454
+ * - Returns empty string if the input is non empty string.
1455
+ *
1456
+ * @param bnDigit - A string containing Bangla (Arabic system) digits.
1457
+ * @param forceNumber - Whether to force number conversion even if the input includes non-digit character(s). Default is `false`.
1458
+ *
1459
+ * @example
1460
+ * banglaToDigit('১২৩abc'); // 123
1461
+ * banglaToDigit(''); // NaN
1462
+ * banglaToDigit('৪৫৬'); // 456
1463
+ *
1464
+ * @example
1465
+ * banglaToDigit('১২৩', false); // "123"
1466
+ * banglaToDigit('১২৩abc', false); // "123abc"
1467
+ */
1468
+ function banglaToDigit(bnDigit, forceNumber = true) {
1469
+ if (!isNonEmptyString(bnDigit)) return forceNumber ? NaN : "";
1470
+ const digitStr = bnDigit.replace(/[০১২৩৪৫৬৭৮৯]/g, (d) => String(BN_DIGITS[d]));
1471
+ if (forceNumber) return Number(digitStr.split("").filter((dig) => !isNaN(Number(dig))).join(""));
1472
+ return digitStr;
1473
+ }
1474
+ /**
1475
+ * * Converts Latin (Arabic system) digits to Bangla digits (Arabic system).
1476
+ *
1477
+ * @remarks
1478
+ * - Accepts numbers or numeric strings including non-digit characters.
1479
+ * - When `preserveNonDigit` is `true`, non-digit characters are preserved in the output.
1480
+ * - When `preserveNonDigit` is `false`, non-numeric strings are stripped.
1481
+ * - Returns empty string for invalid input.
1482
+ *
1483
+ * @param digit - A number or string containing Latin (Arabic system) digits.
1484
+ * @param preserveNonDigit - Whether to preserve non-digit characters in the output. Default is `true`.
1485
+ *
1486
+ * @example
1487
+ * digitToBangla(123); // "১২৩"
1488
+ * digitToBangla('456'); // "৪৫৬"
1489
+ *
1490
+ * @example
1491
+ * digitToBangla('12ab', false); // "১২"
1492
+ * digitToBangla('12ab'); // "১২ab"
1493
+ */
1494
+ function digitToBangla(digit, preserveNonDigit = true) {
1495
+ const banglaDigits = Object.keys(BN_DIGITS);
1496
+ const _matchAndConvert = (value) => {
1497
+ return value.replace(/\d/g, (dig) => banglaDigits[Number(dig)]);
1498
+ };
1499
+ if (isNumber(digit)) return _matchAndConvert(String(digit));
1500
+ if (isNonEmptyString(digit)) {
1501
+ const bnDigStr = _matchAndConvert(digit);
1502
+ if (preserveNonDigit || isNumericString(digit)) return bnDigStr;
1503
+ return bnDigStr.split("").filter((dig) => banglaDigits.includes(dig)).join("");
1504
+ }
1505
+ return "";
1506
+ }
1507
+
1508
+ //#endregion
1509
+ //#region src/number/prime.ts
1510
+ /**
1511
+ * * Checks if a number is prime.
1512
+ *
1513
+ * @param number The number to check.
1514
+ * @returns Boolean: `true` if the number is prime, otherwise `false`.
1515
+ */
1516
+ const isPrime = (number) => {
1517
+ if (number < 2) return false;
1518
+ if (number === 2 || number === 3) return true;
1519
+ if (number % 2 === 0 || number % 3 === 0) return false;
1520
+ for (let i = 5; i * i <= number; i += 6) if (number % i === 0 || number % (i + 2) === 0) return false;
1521
+ return true;
1522
+ };
1523
+ /**
1524
+ * * Find prime numbers in a given range.
1525
+ *
1526
+ * @param start The starting number of the range. Default is `1`.
1527
+ * @param end The ending number of the range. Default is `1000`.
1528
+ * @returns An array of prime numbers within the range (inclusive).
1529
+ */
1530
+ const findPrimeNumbers = (start = 1, end = 1e3) => {
1531
+ let startNumber = start, endNumber = end;
1532
+ if (start > end) [startNumber, endNumber] = [end, start];
1533
+ return Array.from({ length: endNumber - startNumber + 1 }, (_, i) => startNumber + i).filter(isPrime);
1534
+ };
1535
+
1536
+ //#endregion
1537
+ //#region src/array/basics.ts
1538
+ /**
1539
+ * * Flattens a nested array recursively or wraps any non-array data type in an array.
1540
+ *
1541
+ * @param input - The input value, which can be a nested array or a non-array value.
1542
+ * @returns A fully flattened array of type `Flatten<T>`. If the input is not an array, it wraps it in a single-element array.
1543
+ */
1544
+ const flattenArray = (input) => {
1545
+ if (!Array.isArray(input)) return [input];
1546
+ return input.reduce((acc, item) => {
1547
+ return acc.concat(Array.isArray(item) ? flattenArray(item) : [item]);
1548
+ }, []);
1549
+ };
1550
+ /**
1551
+ * @deprecated _Please, use `findAll` instance method from `Finder` class for **more advanced filtering and searching.**_
1552
+ *
1553
+ * * Filters an array of objects based on multiple conditions for specified keys.
1554
+ * @param array - The array of objects to filter.
1555
+ * @param conditions - An object where keys represent the property names and values represent filter conditions.
1556
+ * The conditions can be a function `(value: T[K]) => boolean`.
1557
+ * @returns The filtered array of objects.
1558
+ * @throws `Error` If the input is not a valid array.
1559
+ */
1560
+ const filterArrayOfObjects = (array, conditions) => {
1561
+ if (!Array.isArray(array)) throw new Error("The provided input is not a valid array!");
1562
+ return array?.filter((item) => Object.entries(conditions)?.every(([key, conditionFn]) => {
1563
+ if (typeof conditionFn === "function") return conditionFn(item[key]);
1564
+ return true;
1565
+ }));
1566
+ };
1567
+ /**
1568
+ * * Checks if a value is an empty array or an array with only empty values.
1569
+ *
1570
+ * @param value - The value to check.
1571
+ * @returns `true` if the value is not an array, an empty array, or an array containing only `null`, `undefined`, empty objects, or empty arrays.
1572
+ */
1573
+ const isInvalidOrEmptyArray = (value) => {
1574
+ if (!Array.isArray(value)) return true;
1575
+ if (value?.length === 0) return true;
1576
+ return value?.every((item) => item == null || Array.isArray(item) && item?.length === 0 || typeof item === "object" && Object.keys(item || {})?.length === 0);
1577
+ };
1578
+ /**
1579
+ * * Shuffle the elements of an array.
1580
+ *
1581
+ * @param array Array to shuffle.
1582
+ * @returns Shuffled array.
1583
+ */
1584
+ const shuffleArray = (array) => {
1585
+ if (isInvalidOrEmptyArray(array)) return array;
1586
+ const shuffled = [...array];
1587
+ for (let i = shuffled?.length - 1; i > 0; i--) {
1588
+ const j = Math.floor(Math.random() * (i + 1));
1589
+ [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
1590
+ }
1591
+ return shuffled;
1592
+ };
1593
+ /**
1594
+ * * Get the last element of an array.
1595
+ *
1596
+ * @param array Array to get the last element from.
1597
+ * @returns The last element or `undefined` if the array is empty.
1598
+ */
1599
+ const getLastArrayElement = (array) => {
1600
+ return array?.length > 0 ? array[array?.length - 1] : void 0;
1601
+ };
1602
+
1603
+ //#endregion
1604
+ //#region src/number/range.ts
1605
+ /**
1606
+ * * Function to get numbers within a range based on the provided {@link NumberType} and options.
1607
+ *
1608
+ * @remarks Returns either string or array of numbers based on the {@link RangeOptions.getAsString getAsString} option.
1609
+ *
1610
+ * @param type - The type of numbers to generate ('random', 'prime', etc.).
1611
+ * @param options - Options to configure number generation, including range and formatting.
1612
+ * @returns The numbers in the range based on {@link type} and {@link options} either as string or array of numbers.
1613
+ */
1614
+ function getNumbersInRange(type = "any", options) {
1615
+ const { getAsString = false, min = 0, max = 100, includeMin = true, includeMax = true, separator = ", ", multiplesOf } = options || {};
1616
+ let output = [];
1617
+ /**
1618
+ * Helper function to apply range and get array of numbers in that range.
1619
+ *
1620
+ * @param start The start of the range.
1621
+ * @param end The end of the range.
1622
+ * @returns The array of numbers in the range.
1623
+ */
1624
+ const _applyRangeOptions = (start, end) => {
1625
+ let startNumber = start;
1626
+ let endNumber = end;
1627
+ if (start > end) [startNumber, endNumber] = [end, start];
1628
+ const numbers = [];
1629
+ for (let i = startNumber; i <= endNumber; i++) if (i >= startNumber && i <= endNumber && (includeMin || i > startNumber) && (includeMax || i < endNumber)) numbers.push(i);
1630
+ return numbers;
1631
+ };
1632
+ if (type === "prime" && !isUndefined(multiplesOf)) console.warn("Warning: The \"multiplesOf\" option is ignored when the type is \"prime\"!");
1633
+ switch (type) {
1634
+ case "random":
1635
+ output = shuffleArray(_applyRangeOptions(min, max).map((n) => getRandomNumber({
1636
+ min: n,
1637
+ max: n,
1638
+ includeMin,
1639
+ includeMax
1640
+ })));
1641
+ break;
1642
+ case "prime":
1643
+ output = _applyRangeOptions(min, max).filter(isPrime);
1644
+ break;
1645
+ case "odd":
1646
+ output = _applyRangeOptions(min, max).filter(isOdd);
1647
+ break;
1648
+ case "even":
1649
+ output = _applyRangeOptions(min, max).filter(isEven);
1650
+ break;
1651
+ case "natural":
1652
+ output = _applyRangeOptions(Math.max(min, 1), max);
1653
+ break;
1654
+ default:
1655
+ output = _applyRangeOptions(min, max);
1656
+ break;
1657
+ }
1658
+ if (type !== "prime") output = _applyMultiples(output, multiplesOf);
1659
+ return getAsString ? convertArrayToString(output, { separator }) : output;
1660
+ }
1661
+
1662
+ //#endregion
1663
+ //#region src/array/transform.ts
1664
+ /**
1665
+ * * Converts an array of objects into a formatted array of options.
1666
+ *
1667
+ * @param data - An array of objects to convert into options.
1668
+ * @param config - The configuration object to specify the keys for the `value` (firstFieldName) and `label` (secondFieldName) fields and rename as needed.
1669
+ * @returns An array of options, where each option has `value` and `label` fields as default or as specified by user in the config options.
1670
+ */
1671
+ function createOptionsArray(data, config) {
1672
+ const { firstFieldKey, secondFieldKey, firstFieldName = "value", secondFieldName = "label", retainNumberValue = false } = config || {};
1673
+ if (data && data?.length) return data?.map((datum) => {
1674
+ const firstValue = retainNumberValue && isNumber(datum[firstFieldKey]) ? datum[firstFieldKey] : String(datum[firstFieldKey] ?? "");
1675
+ return {
1676
+ [firstFieldName]: firstValue,
1677
+ [secondFieldName]: String(datum[secondFieldKey] ?? "")
1678
+ };
1679
+ });
1680
+ else return [];
1681
+ }
1682
+ /**
1683
+ * * Removes duplicate values from an array, supporting deep comparison for objects and arrays.
1684
+ *
1685
+ * @param array - The array from which duplicates need to be removed.
1686
+ * @returns A new array with duplicates removed.
1687
+ */
1688
+ function removeDuplicatesFromArray(array) {
1689
+ return array?.filter((item, index, self) => index === self?.findIndex((el) => isDeepEqual(el, item)));
1690
+ }
1691
+ /**
1692
+ * * Finds duplicate values in an array, runs deep comparison for objects and arrays.
1693
+ *
1694
+ * @param array - The array in which to find duplicates.
1695
+ * @returns An array containing all duplicate entries (each one only once).
1696
+ */
1697
+ function getDuplicates(array) {
1698
+ const seen = [];
1699
+ const duplicates = [];
1700
+ for (const item of array) {
1701
+ const hasSeen = seen?.find((el) => isDeepEqual(el, item));
1702
+ const hasDuplicate = duplicates?.find((el) => isDeepEqual(el, item));
1703
+ if (hasSeen && !hasDuplicate) duplicates?.push(item);
1704
+ else if (!hasSeen) seen?.push(item);
1705
+ }
1706
+ return duplicates;
1707
+ }
1708
+ /**
1709
+ * * Finds elements missing from one array compared to another using deep comparison.
1710
+ *
1711
+ * @param options - Configuration to specify which array to compare and direction of check.
1712
+ * @returns An array of missing elements based on the comparison direction.
1713
+ */
1714
+ /**
1715
+ * * Finds elements missing from one array compared to another using deep comparison.
1716
+ *
1717
+ * @param array1 The first array to compare.
1718
+ * @param array2 The second array to compare.
1719
+ * @param missingFrom Which direction to compare for missing values:.
1720
+ * - `'from-first'` → values in `array1` missing in `array2`.
1721
+ * - `'from-second'` → values in `array2` missing in `array1`.
1722
+ * @returns An array of missing elements based on the comparison direction.
1723
+ */
1724
+ function findMissingElements(array1, array2, missingFrom) {
1725
+ const source = (missingFrom === "from-first" ? array1 : array2) ?? [];
1726
+ const target = (missingFrom === "from-first" ? array2 : array1) ?? [];
1727
+ return source.filter((s) => !target?.some((t) => isDeepEqual(t, s)));
1728
+ }
1729
+ /**
1730
+ * * Splits an array into chunks of a given size.
1731
+ *
1732
+ * @param arr The array to split.
1733
+ * @param chunkSize The size of each chunk.
1734
+ * @returns An array of chunked arrays.
1735
+ */
1736
+ function splitArray(arr, chunkSize) {
1737
+ const result = [];
1738
+ for (let i = 0; i < arr?.length; i += chunkSize) result.push(arr.slice(i, i + chunkSize));
1739
+ return result;
1740
+ }
1741
+ /**
1742
+ * * Group an array of objects by a specified key, returning only arrays of grouped objects.
1743
+ *
1744
+ * @param source - The source array of objects to group.
1745
+ * @param property - The property to group the array by. Property can be a string, number, boolean, undefined or null. Supports nested dot notation.
1746
+ *
1747
+ * @returns An array of grouped arrays. Each sub-array contains objects that share the same value for the specified property.
1748
+ *
1749
+ * @example
1750
+ * splitArrayByProperty([{ type: 'a' }, { type: 'b' }, { type: 'a' }, { type: undefined }], 'type')
1751
+ * // => [ [{ type: 'a' }, { type: 'a' }], [{ type: 'b' }], [{ type: undefined }] ]
1752
+ *
1753
+ * @notes
1754
+ * - Returns an empty array if the input is invalid or empty.
1755
+ * - Groups objects even when the group key is `undefined` or `null` (object with `null` & `undefined` property-values are grouped together).
1756
+ */
1757
+ function splitArrayByProperty(source, property) {
1758
+ if (!isValidArray(source)) return [];
1759
+ const grouped = {};
1760
+ source.forEach((item) => {
1761
+ const rawKey = _resolveNestedKey(item, property);
1762
+ const key = rawKey != null ? String(rawKey) : "__undefined__";
1763
+ if (!grouped[key]) grouped[key] = [];
1764
+ grouped[key].push(item);
1765
+ });
1766
+ return Object.values(grouped);
1767
+ }
1768
+ /**
1769
+ * * Rotates an array left or right by a given number of steps.
1770
+ *
1771
+ * @param arr The array to rotate.
1772
+ * @param steps The number of positions to rotate (positive: right, negative: left).
1773
+ * @returns The rotated array.
1774
+ */
1775
+ function rotateArray(arr, steps) {
1776
+ const length = arr?.length;
1777
+ if (length === 0) return arr;
1778
+ const offset = (steps % length + length) % length;
1779
+ return arr.slice(-offset).concat(arr.slice(0, -offset));
1780
+ }
1781
+ /**
1782
+ * * Moves an element within an array from one index to another.
1783
+ *
1784
+ * @param arr The array to modify.
1785
+ * @param fromIndex The index of the element to move.
1786
+ * @param toIndex The new index for the element.
1787
+ * @returns A new array with the element moved.
1788
+ */
1789
+ function moveArrayElement(arr, fromIndex, toIndex) {
1790
+ const newArr = [...arr];
1791
+ const [item] = newArr.splice(fromIndex, 1);
1792
+ newArr.splice(toIndex, 0, item);
1793
+ return newArr;
1794
+ }
1795
+
1796
+ //#endregion
1797
+ //#region src/array/calc.ts
1798
+ /**
1799
+ * * Calculates the sum of differences between two numeric fields for each item in the array.
1800
+ *
1801
+ * @param data - The array of objects to process.
1802
+ * @param first - The field name to subtract **from** (minuend), supports nested dot notation.
1803
+ * @param second - The field name to subtract (subtrahend), supports nested dot notation.
1804
+ * @param roundTo - Decimal places to round the result to. Defaults to 2.
1805
+ * @returns The total sum of differences between the two fields across all items.
1806
+ *
1807
+ * @example
1808
+ * sumFieldDifference([{ buy: 10, sell: 3 }, { buy: 8, sell: 5 }], 'buy', 'sell');
1809
+ * // => 10
1810
+ */
1811
+ function sumFieldDifference(data, first, second, roundTo = 2) {
1812
+ if (!isValidArray(data)) return 0;
1813
+ const total = data?.reduce((acc, item) => {
1814
+ return acc + (_getNumericProp(item, first) - _getNumericProp(item, second));
1815
+ }, 0);
1816
+ return roundNumber(total, roundTo);
1817
+ }
1818
+ /**
1819
+ * * Calculates the total sum of a numeric field across all items.
1820
+ *
1821
+ * @param data - The array of objects to process.
1822
+ * @param field - The field to sum values from. Supports nested dot notation.
1823
+ * @param roundTo - Decimal places to round the result to. Defaults to 2.
1824
+ * @returns The rounded total sum.
1825
+ *
1826
+ * @example
1827
+ * sumByField([{ a: 5 }, { a: 3 }], 'a');
1828
+ * // => 8
1829
+ */
1830
+ function sumByField(data, field, roundTo = 2) {
1831
+ if (!isValidArray(data)) return 0;
1832
+ const total = data?.reduce((acc, item) => acc + _getNumericProp(item, field), 0);
1833
+ return roundNumber(total, roundTo);
1834
+ }
1835
+ /**
1836
+ * * Calculates the average of a numeric field across all items.
1837
+ *
1838
+ * @param data - The array of objects to process.
1839
+ * @param field - The field to calculate the average from. Supports nested dot notation.
1840
+ * @param roundTo - Decimal places to round the result to. Defaults to 2.
1841
+ * @returns The rounded average.
1842
+ *
1843
+ * @example
1844
+ * averageByField([{ a: 4 }, { a: 6 }], 'a');
1845
+ * // => 5
1846
+ */
1847
+ function averageByField(data, field, roundTo = 2) {
1848
+ if (!isValidArray(data)) return 0;
1849
+ const total = data?.reduce((acc, item) => acc + _getNumericProp(item, field), 0);
1850
+ return roundNumber(total / data.length, roundTo);
1851
+ }
1852
+ /**
1853
+ * * Groups an array of objects by a primitive field and sums another numeric field per group.
1854
+ *
1855
+ * @param data - The array of objects to group.
1856
+ * @param groupBy - The field to group by. Supports nested dot notation.
1857
+ * @param sumBy - The numeric field to sum within each group. Supports nested dot notation.
1858
+ * @param roundTo - Decimal places to round each group’s result to. Defaults to 2.
1859
+ * @returns An array of records, each with the group key and its corresponding summed value.
1860
+ *
1861
+ * @example
1862
+ * groupAndSumByField([{ type: 'A', val: 2 }, { type: 'A', val: 3 }, { type: 'B', val: 1 }], 'type', 'val');
1863
+ * // => [{ A: 5 }, { B: 1 }]
1864
+ */
1865
+ function groupAndSumByField(data, groupBy, sumBy, roundTo = 2) {
1866
+ if (!isValidArray(data)) return [];
1867
+ return splitArrayByProperty(data, groupBy).map((group) => ({ [`${_resolveNestedKey(group[0], groupBy)}`]: sumByField(group, sumBy, roundTo) }));
1868
+ }
1869
+ /**
1870
+ * * Groups an array of objects by a primitive field and averages another numeric field per group.
1871
+ *
1872
+ * @param data - The array of objects to group.
1873
+ * @param groupBy - The field to group by. Supports nested dot notation.
1874
+ * @param averageBy - The numeric field to average within each group. Supports nested dot notation.
1875
+ * @param roundTo - Decimal places to round each group’s average to. Defaults to 2.
1876
+ * @returns An array of records, each with the group key and its corresponding average value.
1877
+ *
1878
+ * @example
1879
+ * groupAndAverageByField([{ type: 'A', val: 2 }, { type: 'A', val: 4 }, { type: 'B', val: 6 }], 'type', 'val');
1880
+ * // => [{ A: 3 }, { B: 6 }]
1881
+ */
1882
+ function groupAndAverageByField(data, groupBy, averageBy, roundTo = 2) {
1883
+ if (!isValidArray(data)) return [];
1884
+ return splitArrayByProperty(data, groupBy).map((group) => ({ [`${_resolveNestedKey(group[0], groupBy)}`]: averageByField(group, averageBy, roundTo) }));
1885
+ }
1886
+
1887
+ //#endregion
1888
+ //#region src/array/Finder.ts
1889
+ /**
1890
+ * @class `Finder` performs optimized searching on arrays.
1891
+ * - It supports binary search, fuzzy search, and smart caching with TTL.
1892
+ */
1893
+ var Finder = class Finder {
1894
+ static #DEFAULT_TTL = 1e3 * 60 * 5;
1895
+ #cachedResult = /* @__PURE__ */ new Map();
1896
+ #sortedCache = /* @__PURE__ */ new Map();
1897
+ #ttl;
1898
+ #items;
1899
+ /**
1900
+ * * Creates a new `Finder` instance.
1901
+ *
1902
+ * @param data The initial array of items or a callback returning them.
1903
+ * @param ttl Time-to-live (in milliseconds) for cached search results. Defaults to {@link Finder.#DEFAULT_TTL 5 Minutes}.
1904
+ */
1905
+ constructor(data, ttl = Finder.#DEFAULT_TTL) {
1906
+ this.#ttl = ttl;
1907
+ this.#items = isFunction(data) ? data() : data;
1908
+ }
1909
+ /**
1910
+ * @instance Clears cache globally or for a specific key.
1911
+ * @param key Optional key to clear only a specific cache entry.
1912
+ */
1913
+ clearCache(key) {
1914
+ if (key) this.#cachedResult.delete(key);
1915
+ else this.#cachedResult.clear();
1916
+ }
1917
+ /**
1918
+ * @instance Finds all items that match the provided matcher using optional caching or fuzzy logic.
1919
+ * @param matcher The value to match against.
1920
+ * @param keySelector Property key or selector function.
1921
+ * @param options Optional settings for search behavior and source list.
1922
+ */
1923
+ findAll(matcher, keySelector, options) {
1924
+ const { fuzzy = false, needSorting = true, cacheKey = "finder-cache", forceBinary = false, caseInsensitive = true, data } = options ?? {};
1925
+ const source = isFunction(data) ? data() : data ?? this.#items;
1926
+ if (!source?.length) return [];
1927
+ const rawGetKey = typeof keySelector === "function" ? keySelector : (item) => item[keySelector];
1928
+ const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
1929
+ const normalizedMatcher = caseInsensitive && isString(matcher) ? matcher.toLowerCase() : matcher;
1930
+ if (cacheKey) {
1931
+ const entry = this.#cachedResult.get(cacheKey);
1932
+ if (entry && Date.now() - entry.timestamp < this.#ttl) return entry.result;
1933
+ else this.#cachedResult.delete(cacheKey);
1934
+ }
1935
+ let results = [];
1936
+ if (source.length < 100 && !forceBinary) results = source.filter((item) => {
1937
+ const key = getKey(item);
1938
+ return (caseInsensitive && isString(key) ? key.toLowerCase() : key) === normalizedMatcher;
1939
+ });
1940
+ else {
1941
+ const sorted = needSorting ? this.#sortAndCache(source, getKey, cacheKey) : source;
1942
+ const firstMatch = this.binarySearch(sorted, normalizedMatcher, getKey, caseInsensitive);
1943
+ if (firstMatch) {
1944
+ const baseKey = getKey(firstMatch);
1945
+ const base = caseInsensitive && isString(baseKey) ? baseKey.toLowerCase() : baseKey;
1946
+ results = sorted.filter((item) => {
1947
+ const key = getKey(item);
1948
+ return (caseInsensitive && isString(key) ? key.toLowerCase() : key) === base;
1949
+ });
1950
+ }
1951
+ }
1952
+ if (!results.length && fuzzy && isString(normalizedMatcher)) results = source.filter((item) => {
1953
+ const rawKey = getKey(item);
1954
+ const key = caseInsensitive && isString(rawKey) ? rawKey.toLowerCase() : String(rawKey);
1955
+ return this.#match(key, normalizedMatcher);
1956
+ });
1957
+ if (cacheKey) this.#cachedResult.set(cacheKey, {
1958
+ result: results,
1959
+ timestamp: Date.now()
1960
+ });
1961
+ return results;
1962
+ }
1963
+ /**
1964
+ * @instance Finds first matching item that matches the provided matcher using optional caching or fuzzy logic.
1965
+ * @param matcher The value to match.
1966
+ * @param keySelector Property key or selector function.
1967
+ * @param options Optional behavior flags and item source.
1968
+ */
1969
+ findOne(matcher, keySelector, options) {
1970
+ const { fuzzy = false, needSorting = true, cacheKey = "finder-cache", forceBinary = false, caseInsensitive = true, data } = options ?? {};
1971
+ const source = isFunction(data) ? data() : data ?? this.#items;
1972
+ if (!source?.length) return void 0;
1973
+ const rawGetKey = typeof keySelector === "function" ? keySelector : (item) => item[keySelector];
1974
+ const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
1975
+ const normalizedMatcher = caseInsensitive && isString(matcher) ? matcher.toLowerCase() : matcher;
1976
+ if (cacheKey) {
1977
+ const entry = this.#cachedResult.get(cacheKey);
1978
+ if (entry && Date.now() - entry.timestamp < this.#ttl) return entry.result[0];
1979
+ else this.#cachedResult.delete(cacheKey);
1980
+ }
1981
+ let result;
1982
+ if (source?.length < 100 && !forceBinary) result = source?.find((item) => {
1983
+ const key = getKey(item);
1984
+ return (caseInsensitive && isString(key) ? key.toLowerCase() : key) === normalizedMatcher;
1985
+ });
1986
+ else result = this.binarySearch(needSorting ? this.#sortAndCache(source, getKey, cacheKey) : source, normalizedMatcher, getKey, caseInsensitive);
1987
+ if (!result && fuzzy && isString(normalizedMatcher)) return this.fuzzySearch(source, normalizedMatcher, getKey, caseInsensitive);
1988
+ if (cacheKey && result) this.#cachedResult.set(cacheKey, {
1989
+ result: [result],
1990
+ timestamp: Date.now()
1991
+ });
1992
+ return result;
1993
+ }
1994
+ /**
1995
+ * @instance Asynchronous variant of `findAll` that accepts a promise-based data supplier.
1996
+ * @param supplier Async function resolving the items list.
1997
+ * @param matcher The value to match.
1998
+ * @param keySelector Property key or selector function.
1999
+ * @param options Optional settings for search behavior and cache.
2000
+ */
2001
+ async findAllAsync(supplier, matcher, keySelector, options) {
2002
+ const items = await supplier();
2003
+ return this.findAll(matcher, keySelector, {
2004
+ ...options,
2005
+ data: items
2006
+ });
2007
+ }
2008
+ /**
2009
+ * @instance Asynchronous variant of `findOne`.
2010
+ * @param supplier Async function resolving the items list.
2011
+ * @param matcher The value to match.
2012
+ * @param keySelector Property key or selector function.
2013
+ * @param options Optional settings for behavior and cache.
2014
+ */
2015
+ async findOneAsync(supplier, matcher, keySelector, options) {
2016
+ const items = await supplier();
2017
+ return this.findOne(matcher, keySelector, {
2018
+ ...options,
2019
+ data: items
2020
+ });
2021
+ }
2022
+ /**
2023
+ * @instance Performs a binary search on a sorted array using a custom key selector.
2024
+ *
2025
+ * @param sorted - The sorted array of items to search.
2026
+ * @param matcher - The value to search for.
2027
+ * @param keySelector - A function that extracts the comparable key from each item.
2028
+ * @param caseInsensitive - Whether to compare string keys ignoring case.
2029
+ * @returns The first matching item if found; otherwise, undefined.
2030
+ */
2031
+ binarySearch(sorted, matcher, keySelector, caseInsensitive) {
2032
+ let min = 0, max = sorted?.length - 1;
2033
+ while (min <= max) {
2034
+ const mid = Math.floor((min + max) / 2);
2035
+ const midKey = keySelector(sorted[mid]);
2036
+ const key = caseInsensitive && isString(midKey) ? midKey.toLowerCase() : midKey;
2037
+ if (key === matcher) return sorted[mid];
2038
+ if (key < matcher) min = mid + 1;
2039
+ else max = mid - 1;
2040
+ }
2041
+ }
2042
+ /**
2043
+ * @instance Performs a fuzzy search on an array by matching characters in sequence.
2044
+ *
2045
+ * @param array - The array of items to search.
2046
+ * @param matcher - The fuzzy search string to match against.
2047
+ * @param keySelector - A function that extracts the key to search from each item.
2048
+ * @param caseInsensitive - Whether to compare ignoring case for string values.
2049
+ * @returns The first fuzzy-matching item if found; otherwise, undefined.
2050
+ */
2051
+ fuzzySearch(array, matcher, keySelector, caseInsensitive) {
2052
+ for (const item of array) {
2053
+ const rawKey = keySelector(item);
2054
+ const key = caseInsensitive && isString(rawKey) ? rawKey.toLowerCase() : String(rawKey);
2055
+ if (this.#match(key, matcher)) return item;
2056
+ }
2057
+ }
2058
+ /**
2059
+ * @private Checks if the characters in the target string appear in order within the source string.
2060
+ * @param source Source string to search within.
2061
+ * @param target Target string to match against the source string.
2062
+ * @returns True if the target string is a fuzzy match within the source string; otherwise, false.
2063
+ */
2064
+ #match(source, target) {
2065
+ let i = 0;
2066
+ for (const char of target) {
2067
+ i = source?.indexOf(char, i);
2068
+ if (i === -1) return false;
2069
+ i++;
2070
+ }
2071
+ return true;
2072
+ }
2073
+ /**
2074
+ * @private Sorts an array and caches the result for a specified time-to-live (TTL).
2075
+ * @param data Data to sort and cache.
2076
+ * @param getKey Key extraction function.
2077
+ * @param cacheKey Optional cache key for storing the result.
2078
+ * @returns
2079
+ */
2080
+ #sortAndCache(data, getKey, cacheKey) {
2081
+ if (cacheKey) {
2082
+ const entry = this.#sortedCache.get(cacheKey);
2083
+ if (entry && Date.now() - entry.timestamp < this.#ttl) return entry.result;
2084
+ else this.#sortedCache.delete(cacheKey);
2085
+ }
2086
+ const sorted = [...data].sort((a, b) => {
2087
+ const keyA = getKey(a);
2088
+ const keyB = getKey(b);
2089
+ return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;
2090
+ });
2091
+ if (cacheKey) this.#sortedCache.set(cacheKey, {
2092
+ result: sorted,
2093
+ timestamp: Date.now()
2094
+ });
2095
+ return sorted;
2096
+ }
2097
+ /**
2098
+ * @static @private Creates a memoized version of a key extractor.
2099
+ * @param getKey Original key extraction function
2100
+ */
2101
+ static #createMemoizedKeyGetter(getKey) {
2102
+ const cache = /* @__PURE__ */ new Map();
2103
+ return (item) => {
2104
+ if (cache.has(item)) return cache.get(item);
2105
+ const key = getKey(item);
2106
+ cache.set(item, key);
2107
+ return key;
2108
+ };
2109
+ }
2110
+ };
2111
+
2112
+ //#endregion
2113
+ //#region src/object/basics.ts
2114
+ /**
2115
+ * * Deep clone an object using `structuredClone` or deterministic *JSON serialization*.
2116
+ *
2117
+ * @param obj Object to clone.
2118
+ * @param serialize Whether to force deterministic JSON serialization instead of using `structuredClone`. Defaults to `false`.
2119
+ * @returns Deep cloned object.
2120
+ *
2121
+ * @remarks
2122
+ * **Primary behavior**
2123
+ * - By default (`serialize = false`), the function uses {@link https://developer.mozilla.org/docs/Web/API/Window/structuredClone structuredClone} when available. This supports:
2124
+ * - Circular references
2125
+ * - `Date` objects
2126
+ * - `Map` / `Set`
2127
+ * - `RegExp`
2128
+ * - Typed arrays
2129
+ * - Most built-in JavaScript types
2130
+ * - Preserves `undefined` values
2131
+ *
2132
+ * - **Note:** `structuredClone` **does not preserve class prototypes**, even though it preserves data types like `Date`, `Map`, and `Set`.
2133
+ *
2134
+ * **Deterministic serialization mode**
2135
+ * - When `serialize = true`, or when `structuredClone` is unavailable, the function falls back to **stable JSON serialization** via `stableStringify`. This guarantees:
2136
+ * - All object keys are sorted alphabetically.
2137
+ * - Consistent output across environments (deterministic).
2138
+ * - All `undefined` values are converted to `null`.
2139
+ * - Converting date-like objects (`Date`, `Chronos`, `Moment.js`, `Day.js`, `Luxon`, `JS-Joda`, `Temporal`) **in the same way that {@link JSON.stringify} would serialize them**, ensuring predictable and JSON-compliant output.
2140
+ *
2141
+ * - This mode is ideal for:
2142
+ * - Hashing
2143
+ * - Signature generation
2144
+ * - Deep equality checks
2145
+ * - Anything requiring deterministic, environment-neutral output
2146
+ *
2147
+ * **Deterministic mode limitations**
2148
+ * - JSON serialization will:
2149
+ * - Drop functions and `Symbol` values.
2150
+ * - Lose prototype and class instance information.
2151
+ * - Convert all date-like objects into strings.
2152
+ * - Fail on circular references.
2153
+ *
2154
+ * **Final safety fallback**
2155
+ * - If JSON serialization fails (e.g., due to circular references), the function returns a **shallow clone** (`{ ...obj }`) to ensure the cloning never throws.
2156
+ */
2157
+ function cloneObject(obj, serialize = false) {
2158
+ try {
2159
+ if (!serialize && typeof structuredClone === "function") return structuredClone(obj);
2160
+ return JSON.parse(stableStringify(obj));
2161
+ } catch {
2162
+ return { ...obj };
2163
+ }
2164
+ }
2165
+ /**
2166
+ * * Count the number of fields in an object.
2167
+ *
2168
+ * @param obj Object to check.
2169
+ * @returns Number of fields in the object.
2170
+ */
2171
+ function countObjectFields(obj) {
2172
+ if (obj != null) return Object.keys(obj)?.length;
2173
+ return 0;
2174
+ }
2175
+ function extractObjectKeys(obj, tuple) {
2176
+ const keys = isNotEmptyObject(obj) ? Object.keys(obj) : [];
2177
+ return tuple ? keys : keys;
2178
+ }
2179
+ /**
2180
+ * * Recursively extracts all nested keys from an object as an array.
2181
+ *
2182
+ * @remarks
2183
+ * - Returns an empty array (`[]`) for an empty object or a non-object value.
2184
+ * - For only top-level keys, use {@link extractObjectKeys}.
2185
+ *
2186
+ * @param obj The object from which to extract the keys.
2187
+ * @returns An array of all the nested keys (string literals) from the specified object.
2188
+ */
2189
+ function extractObjectKeysDeep(obj) {
2190
+ function _getDeepKeys(candidate) {
2191
+ let result = [];
2192
+ for (const key in candidate) {
2193
+ result.push(key);
2194
+ if (isNotEmptyObject(candidate[key])) result = [...result, ..._getDeepKeys(candidate[key])];
2195
+ }
2196
+ return result;
2197
+ }
2198
+ return isNotEmptyObject(obj) ? _getDeepKeys(obj) : [];
2199
+ }
2200
+
2201
+ //#endregion
2202
+ //#region src/object/convert.ts
2203
+ /**
2204
+ * * Converts the values of specified keys in an object or array of objects to either string or number.
2205
+ * * Supports nested objects using dot-notation keys.
2206
+ *
2207
+ * @param data The object or array of objects to convert.
2208
+ * @param options Options object specifying the conversion mapping.
2209
+ * - `keys`: The keys in the object to be converted (dot-notation supported).
2210
+ * - `convertTo`: The target type, either "string" or "number".
2211
+ * @returns The modified object or array of objects with the converted values, with updated types.
2212
+ */
2213
+ function convertObjectValues(data, options) {
2214
+ const { keys, convertTo } = options || {};
2215
+ /** * Helper function to resolve a dot-notation key path and modify the corresponding value in the object. */
2216
+ const _setValueAtPath = (obj, path, convertTo) => {
2217
+ const segments = path.split(".");
2218
+ let current = obj;
2219
+ segments?.forEach((key, index) => {
2220
+ if (index === segments?.length - 1) {
2221
+ const value = current?.[key];
2222
+ if (convertTo === "string" && !isString(value)) current[key] = String(value);
2223
+ else if (convertTo === "number" && !isNumber(value)) current[key] = Number(value);
2224
+ } else if (isObject(current?.[key])) current = current?.[key];
2225
+ else {
2226
+ current[key] = {};
2227
+ current = current?.[key];
2228
+ }
2229
+ });
2230
+ return obj;
2231
+ };
2232
+ /** * Recursively process a single object. */
2233
+ const _convertValue = (obj) => {
2234
+ let newObj = { ...obj };
2235
+ keys?.forEach((key) => {
2236
+ newObj = _setValueAtPath(newObj, key, convertTo);
2237
+ });
2238
+ return newObj;
2239
+ };
2240
+ if (Array.isArray(data)) return data?.map(_convertValue);
2241
+ return _convertValue(data);
2242
+ }
2243
+ /**
2244
+ * * Pick specific fields from an object and create a new object with specified fields.
2245
+ *
2246
+ * @description This function creates a new object containing only the specified fields from the source object.
2247
+ * It is useful for creating a new object with a subset of properties from an existing object.
2248
+ *
2249
+ * @param T The type of the source object.
2250
+ * @param U The type of the keys to pick from the source object.
2251
+ *
2252
+ * @param source The source object from which to pick fields.
2253
+ * @param keys The keys of the fields to pick from the source object.
2254
+ *
2255
+ * @returns An object containing only the picked fields.
2256
+ */
2257
+ function pickFields(source, keys) {
2258
+ const result = {};
2259
+ keys?.forEach((key) => {
2260
+ result[key] = source?.[key];
2261
+ });
2262
+ return result;
2263
+ }
2264
+ /**
2265
+ * * Create a new object by removing specific keys from the source object.
2266
+ *
2267
+ * @param source - The original (source) object from which to delete fields.
2268
+ * @param keys - An array of keys (fields) to remove from the object.
2269
+ *
2270
+ * @returns A new object without the specified keys.
2271
+ *
2272
+ * @example
2273
+ * deleteFields({ a: 1, b: 2, c: 3 }, ['b'])
2274
+ * // => { a: 1, c: 3 }
2275
+ *
2276
+ * @notes
2277
+ * - Does not mutate the original object.
2278
+ * - Useful for excluding sensitive or unwanted fields.
2279
+ */
2280
+ function deleteFields(source, keys) {
2281
+ const result = {};
2282
+ for (const key in source) if (!keys.includes(key)) result[key] = source?.[key];
2283
+ return result;
2284
+ }
2285
+ /**
2286
+ * * Pick specific fields from an object based on a given condition.
2287
+ *
2288
+ * @description This function creates a new object containing only the fields that satisfy the given condition.
2289
+ * The condition can be based on the field's value or type, depending on the implementation.
2290
+ *
2291
+ * @param T The type of the source object.
2292
+ *
2293
+ * @param source The source object from which to pick fields.
2294
+ * @param condition A function that takes the key and value of a property and returns a boolean indicating whether the property should be picked.
2295
+ *
2296
+ * @returns An object containing only the fields that satisfy the condition.
2297
+ */
2298
+ function pickObjectFieldsByCondition(source, condition) {
2299
+ const result = {};
2300
+ Object.entries(source)?.forEach(([key, value]) => {
2301
+ if (condition(key, value)) result[key] = value;
2302
+ });
2303
+ return result;
2304
+ }
2305
+ /**
2306
+ * * Remap fields from one object to another.
2307
+ * @description This function creates a new object with fields remapped from the source object to the target object based on the provided field map.
2308
+ *
2309
+ * @param source The source object from which to remap fields.
2310
+ * @param fieldMap An object that maps target keys to source keys.
2311
+ * @returns An object with fields remapped according to the field map.
2312
+ */
2313
+ function remapFields(source, fieldMap) {
2314
+ const result = {};
2315
+ for (const targetKey in fieldMap) {
2316
+ const sourceKey = fieldMap?.[targetKey];
2317
+ result[targetKey] = source?.[sourceKey];
2318
+ }
2319
+ return result;
2320
+ }
2321
+
2322
+ //#endregion
2323
+ //#region src/utils/xtras.ts
2324
+ /**
2325
+ * @function `getCountryByPhone` Get country details by matching the country code in the given phone number.
2326
+ * @param phone - The phone number to look up, can be a string or a number. It will be normalized by removing any non-digit character before matching against the country codes.
2327
+ * @returns An array of country details that match the provided phone number. Each country detail includes the country name, country code, ISO short code, and ISO code. If the input is invalid (not a primitive value or an empty string), an empty array will be returned.
2328
+ *
2329
+ * @remarks
2330
+ * - The function uses the {@link COUNTRIES} constant, which is an array of country details, to find matches based on the normalized phone number.
2331
+ * - The normalization process removes any non-digit character from the input phone number to ensure consistent matching against the country codes.
2332
+ * - The function checks if the input is neither an empty string nor a finite number before proceeding with the normalization and matching process. If the input is invalid, it returns an empty array.
2333
+ * - If multiple countries share the same country code, all matching countries will be included in the returned array.
2334
+ * - **`IMPORTANT:`** If a phone number does not start with a country code (plain local number), the function will return an empty array since it cannot determine the country. But if the number matches the country code of any country, it will return the corresponding country details even if the number is a local number.
2335
+ *
2336
+ * @example
2337
+ * ```typescript
2338
+ * const country = getCountryByPhone('+8801623732187');
2339
+ * console.info(country);
2340
+ * // Output: [
2341
+ * // {
2342
+ * // country_name: 'Bangladesh',
2343
+ * // country_code: '880',
2344
+ * // iso_code_short: 'BD',
2345
+ * // iso_code: 'BGD'
2346
+ * // }
2347
+ * // ]
2348
+ * ```
2349
+ */
2350
+ function getCountryByPhone(phone) {
2351
+ if (!isNonEmptyString(phone) && !isNumber(phone)) return [];
2352
+ const normalized = (isString(phone) ? phone : String(phone)).replace(/\D/g, "");
2353
+ return COUNTRIES.filter((country) => normalized.startsWith(country.country_code.replace(/-/g, "")));
2354
+ }
2355
+
2356
+ //#endregion
2357
+ export { Currency, Finder, Unit, Unit as UnitConverter, convertToRomanNumerals as arabicToRoman, convertToRomanNumerals, convertToRomanNumerals as integerToRoman, convertToRomanNumerals as numberToRoman, convertToRomanNumerals as numericToRoman, convertToRomanNumerals as toRoman, convertToRomanNumerals as toRomanNumeral, averageByField, averageByField as avgByField, banglaToDigit, getAverage as calculateAverage, getAverage, getAverage as getAverageOfNumbers, factorial as calculateFactorial, factorial, factorial as getFactorial, calculateHCF as calculateGCD, calculateHCF, calculateLCM as calculateLCD, calculateLCM, calculatePercentage, capitalizeString, getOrdinal as cardinalToOrdinal, getOrdinal as convertNumberToOrdinal, getOrdinal as convertToOrdinal, getOrdinal, getOrdinal as getOrdinalNumber, getOrdinal as numberToOrdinal, numberToWordsOrdinal as cardinalWordsToOrdinal, numberToWordsOrdinal as convertNumberToWordsOrdinal, numberToWordsOrdinal, clampNumber, cloneObject, naturalSort as compareNaturally, naturalSort as compareSorter, naturalSort, naturalSort as naturalSortForString, computeTextDiff, convertArrayToString, convertArrayToString as joinArrayElements, formatCurrency as convertNumberToCurrency, formatCurrency, numberToWords as convertNumberToWords, numberToWords, convertObjectValues, romanToInteger as convertRomanToArabic, romanToInteger as convertRomanToInteger, romanToInteger as convertRomanToNumeric, romanToInteger as romanToArabic, romanToInteger, romanToInteger as romanToNumeric, convertStringCase, convertToDecimal, convertToDecimal as convertToFixed, wordsToNumber as convertWordToNumber, wordsToNumber as convertWordsToNumber, wordsToNumber as wordToNumber, wordsToNumber, countInstanceMethods, countInstanceMethods as getInstanceMethodsCount, countObjectFields, countStaticMethods, countStaticMethods as getStaticMethodsCount, countWords, countWords as countWordsInString, countWords as wordCount, createOptionsArray, debounceAction, deepParsePrimitives, deepParsePrimitives as parsePrimitivesDeep, definePrototypeMethod, deleteFields, deleteFields as deleteObjectFields, deleteFields as omitFields, deleteFields as omitObjectFields, deleteFields as removeFields, deleteFields as removeObjectFields, digitToBangla, getDuplicates as extractDuplicates, getDuplicates as extractDuplicatesFromArray, getDuplicates, getDuplicates as getDuplicatesFromArray, extractEmails, extractObjectKeys as extractKeys, extractObjectKeys, extractObjectKeysDeep as extractKeysDeep, extractObjectKeysDeep, findMissingElements as extractMissingElements, findMissingElements, findMissingElements as getMissingElements, extractNewFields, extractNumbersFromString as extractNumbers, extractNumbersFromString, extractNumbersFromString as parseNumbersFromText, extractURLs, extractUpdatedAndNewFields, extractUpdatedFields, getFactors as factorsOf, getFactors as getDivisors, getFactors, fibonacciGenerator, fibonacciGenerator as generateFibonacci, filterArrayOfObjects, findPrimeNumbers, findPrimeNumbers as getPrimeNumbers, flattenArray, flattenObjectDotNotation, flattenObjectKeyValue, formatUnitWithPlural as formatNumberWithPluralUnit, formatUnitWithPlural, formatUnitWithPlural as formatWithPlural, generateAnagrams, generateRandomID, getCharacterDifferences, getClassDetails, getCountryByPhone, getFibonacciSeries as getFibonacci, getFibonacciSeries as getFibonacciNumbers, getFibonacciSeries, getFibonacciSeriesMemo, getFibonacciSeriesMemo as getMemoizedFibonacci, getFibonacciSeriesMemo as getMemoizedFibonacciSeries, getInstanceGetterNames, getInstanceMethodNames, getLastArrayElement, getLevenshteinDistance, getLevenshteinDistance as levenshteinDistance, getNthFibonacci, getNumbersInRange, getRandomFloat as getRandomDecimal, getRandomFloat, getRandomNumber as getRandomInt, getRandomNumber, getStaticGetterNames, getStaticMethodNames, sumNumbers as getSumOfNumbers, sumNumbers, sumNumbers as sumOfNumbers, groupAndAverageByField, groupAndAverageByField as groupAndAvgByField, groupAndSumByField, splitArrayByProperty as groupArrayByProperty, splitArrayByProperty, isDeepEqual, isInvalidOrEmptyArray, isInvalidOrEmptyArray as isValidEmptyArray, isPrime, isPrime as isPrimeNumber, maskString, mergeAndFlattenObjects, mergeObjects, moveArrayElement, normalizeNumber, normalizeString, parseJSON, parseJSON as parseJsonDeep, parseJsonToObject, parseObjectValues, parseObjectValues as parseStringifiedObjectValues, pickFields, pickFields as pickObjectFields, pickObjectFieldsByCondition as pickFieldsByCondition, pickObjectFieldsByCondition, remapFields, remapFields as remapObjectFields, removeDuplicatesFromArray as removeDuplicates, removeDuplicatesFromArray, replaceAllInString, reverseNumber, reverseString, rotateArray, roundNumber, roundNumber as roundToDecimal, roundToNearest as roundNumberToNearestInterval, roundToNearest, roundToNearest as roundToNearestInterval, sanitizeData, shuffleArray, slugifyString, sortAnArray, splitArray, stableStringify, stripJsonEdgeGarbage, sumByField, sumDigits, sumFieldDifference, sumFieldDifference as totalDeltaByField, throttleAction, trimString, truncateString };