ts-data-forge 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/README.md +1 -1
  2. package/dist/array/array-utils.d.mts +2617 -0
  3. package/dist/array/array-utils.d.mts.map +1 -0
  4. package/dist/array/array-utils.mjs +2915 -0
  5. package/dist/array/array-utils.mjs.map +1 -0
  6. package/dist/array/index.d.mts +3 -0
  7. package/dist/array/index.d.mts.map +1 -0
  8. package/dist/array/index.mjs +3 -0
  9. package/dist/array/index.mjs.map +1 -0
  10. package/dist/array/tuple-utils.d.mts +421 -0
  11. package/dist/array/tuple-utils.d.mts.map +1 -0
  12. package/dist/array/tuple-utils.mjs +391 -0
  13. package/dist/array/tuple-utils.mjs.map +1 -0
  14. package/dist/collections/imap-mapped.d.mts +445 -0
  15. package/dist/collections/imap-mapped.d.mts.map +1 -0
  16. package/dist/collections/imap-mapped.mjs +424 -0
  17. package/dist/collections/imap-mapped.mjs.map +1 -0
  18. package/dist/collections/imap.d.mts +359 -0
  19. package/dist/collections/imap.d.mts.map +1 -0
  20. package/dist/collections/imap.mjs +338 -0
  21. package/dist/collections/imap.mjs.map +1 -0
  22. package/dist/collections/index.d.mts +7 -0
  23. package/dist/collections/index.d.mts.map +1 -0
  24. package/dist/collections/index.mjs +7 -0
  25. package/dist/collections/index.mjs.map +1 -0
  26. package/dist/collections/iset-mapped.d.mts +576 -0
  27. package/dist/collections/iset-mapped.d.mts.map +1 -0
  28. package/dist/collections/iset-mapped.mjs +522 -0
  29. package/dist/collections/iset-mapped.mjs.map +1 -0
  30. package/dist/collections/iset.d.mts +426 -0
  31. package/dist/collections/iset.d.mts.map +1 -0
  32. package/dist/collections/iset.mjs +437 -0
  33. package/dist/collections/iset.mjs.map +1 -0
  34. package/dist/collections/queue.d.mts +190 -0
  35. package/dist/collections/queue.d.mts.map +1 -0
  36. package/dist/collections/queue.mjs +317 -0
  37. package/dist/collections/queue.mjs.map +1 -0
  38. package/dist/collections/stack.d.mts +210 -0
  39. package/dist/collections/stack.d.mts.map +1 -0
  40. package/dist/collections/stack.mjs +353 -0
  41. package/dist/collections/stack.mjs.map +1 -0
  42. package/dist/expect-type.d.mts +199 -0
  43. package/dist/expect-type.d.mts.map +1 -0
  44. package/dist/expect-type.mjs +201 -0
  45. package/dist/expect-type.mjs.map +1 -0
  46. package/dist/functional/index.d.mts +5 -0
  47. package/dist/functional/index.d.mts.map +1 -0
  48. package/dist/functional/index.mjs +5 -0
  49. package/dist/functional/index.mjs.map +1 -0
  50. package/dist/functional/match.d.mts +215 -0
  51. package/dist/functional/match.d.mts.map +1 -0
  52. package/dist/functional/match.mjs +139 -0
  53. package/dist/functional/match.mjs.map +1 -0
  54. package/dist/functional/optional.d.mts +517 -0
  55. package/dist/functional/optional.d.mts.map +1 -0
  56. package/dist/functional/optional.mjs +532 -0
  57. package/dist/functional/optional.mjs.map +1 -0
  58. package/dist/functional/pipe.d.mts +185 -0
  59. package/dist/functional/pipe.d.mts.map +1 -0
  60. package/dist/functional/pipe.mjs +129 -0
  61. package/dist/functional/pipe.mjs.map +1 -0
  62. package/dist/functional/result.d.mts +796 -0
  63. package/dist/functional/result.d.mts.map +1 -0
  64. package/dist/functional/result.mjs +844 -0
  65. package/dist/functional/result.mjs.map +1 -0
  66. package/dist/globals.d.mts +38 -0
  67. package/dist/guard/has-key.d.mts +100 -0
  68. package/dist/guard/has-key.d.mts.map +1 -0
  69. package/dist/guard/has-key.mjs +94 -0
  70. package/dist/guard/has-key.mjs.map +1 -0
  71. package/dist/guard/index.d.mts +8 -0
  72. package/dist/guard/index.d.mts.map +1 -0
  73. package/dist/guard/index.mjs +8 -0
  74. package/dist/guard/index.mjs.map +1 -0
  75. package/dist/guard/is-non-empty-string.d.mts +106 -0
  76. package/dist/guard/is-non-empty-string.d.mts.map +1 -0
  77. package/dist/guard/is-non-empty-string.mjs +108 -0
  78. package/dist/guard/is-non-empty-string.mjs.map +1 -0
  79. package/dist/guard/is-non-null-object.d.mts +105 -0
  80. package/dist/guard/is-non-null-object.d.mts.map +1 -0
  81. package/dist/guard/is-non-null-object.mjs +108 -0
  82. package/dist/guard/is-non-null-object.mjs.map +1 -0
  83. package/dist/guard/is-primitive.d.mts +146 -0
  84. package/dist/guard/is-primitive.d.mts.map +1 -0
  85. package/dist/guard/is-primitive.mjs +161 -0
  86. package/dist/guard/is-primitive.mjs.map +1 -0
  87. package/dist/guard/is-record.d.mts +151 -0
  88. package/dist/guard/is-record.d.mts.map +1 -0
  89. package/dist/guard/is-record.mjs +155 -0
  90. package/dist/guard/is-record.mjs.map +1 -0
  91. package/dist/guard/is-type.d.mts +430 -0
  92. package/dist/guard/is-type.d.mts.map +1 -0
  93. package/dist/guard/is-type.mjs +432 -0
  94. package/dist/guard/is-type.mjs.map +1 -0
  95. package/dist/guard/key-is-in.d.mts +158 -0
  96. package/dist/guard/key-is-in.d.mts.map +1 -0
  97. package/dist/guard/key-is-in.mjs +160 -0
  98. package/dist/guard/key-is-in.mjs.map +1 -0
  99. package/dist/index.d.mts +11 -0
  100. package/dist/index.d.mts.map +1 -0
  101. package/dist/index.mjs +61 -0
  102. package/dist/index.mjs.map +1 -0
  103. package/dist/iterator/index.d.mts +2 -0
  104. package/dist/iterator/index.d.mts.map +1 -0
  105. package/dist/iterator/index.mjs +2 -0
  106. package/dist/iterator/index.mjs.map +1 -0
  107. package/dist/iterator/range.d.mts +97 -0
  108. package/dist/iterator/range.d.mts.map +1 -0
  109. package/dist/iterator/range.mjs +130 -0
  110. package/dist/iterator/range.mjs.map +1 -0
  111. package/dist/json/index.d.mts +2 -0
  112. package/dist/json/index.d.mts.map +1 -0
  113. package/dist/json/index.mjs +2 -0
  114. package/dist/json/index.mjs.map +1 -0
  115. package/dist/json/json.d.mts +597 -0
  116. package/dist/json/json.d.mts.map +1 -0
  117. package/dist/json/json.mjs +687 -0
  118. package/dist/json/json.mjs.map +1 -0
  119. package/dist/number/branded-types/finite-number.d.mts +291 -0
  120. package/dist/number/branded-types/finite-number.d.mts.map +1 -0
  121. package/dist/number/branded-types/finite-number.mjs +296 -0
  122. package/dist/number/branded-types/finite-number.mjs.map +1 -0
  123. package/dist/number/branded-types/index.d.mts +27 -0
  124. package/dist/number/branded-types/index.d.mts.map +1 -0
  125. package/dist/number/branded-types/index.mjs +27 -0
  126. package/dist/number/branded-types/index.mjs.map +1 -0
  127. package/dist/number/branded-types/int.d.mts +242 -0
  128. package/dist/number/branded-types/int.d.mts.map +1 -0
  129. package/dist/number/branded-types/int.mjs +239 -0
  130. package/dist/number/branded-types/int.mjs.map +1 -0
  131. package/dist/number/branded-types/int16.d.mts +162 -0
  132. package/dist/number/branded-types/int16.d.mts.map +1 -0
  133. package/dist/number/branded-types/int16.mjs +141 -0
  134. package/dist/number/branded-types/int16.mjs.map +1 -0
  135. package/dist/number/branded-types/int32.d.mts +155 -0
  136. package/dist/number/branded-types/int32.d.mts.map +1 -0
  137. package/dist/number/branded-types/int32.mjs +142 -0
  138. package/dist/number/branded-types/int32.mjs.map +1 -0
  139. package/dist/number/branded-types/non-negative-finite-number.d.mts +165 -0
  140. package/dist/number/branded-types/non-negative-finite-number.d.mts.map +1 -0
  141. package/dist/number/branded-types/non-negative-finite-number.mjs +160 -0
  142. package/dist/number/branded-types/non-negative-finite-number.mjs.map +1 -0
  143. package/dist/number/branded-types/non-negative-int16.d.mts +160 -0
  144. package/dist/number/branded-types/non-negative-int16.d.mts.map +1 -0
  145. package/dist/number/branded-types/non-negative-int16.mjs +138 -0
  146. package/dist/number/branded-types/non-negative-int16.mjs.map +1 -0
  147. package/dist/number/branded-types/non-negative-int32.d.mts +156 -0
  148. package/dist/number/branded-types/non-negative-int32.d.mts.map +1 -0
  149. package/dist/number/branded-types/non-negative-int32.mjs +138 -0
  150. package/dist/number/branded-types/non-negative-int32.mjs.map +1 -0
  151. package/dist/number/branded-types/non-zero-finite-number.d.mts +154 -0
  152. package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -0
  153. package/dist/number/branded-types/non-zero-finite-number.mjs +160 -0
  154. package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -0
  155. package/dist/number/branded-types/non-zero-int.d.mts +131 -0
  156. package/dist/number/branded-types/non-zero-int.d.mts.map +1 -0
  157. package/dist/number/branded-types/non-zero-int.mjs +128 -0
  158. package/dist/number/branded-types/non-zero-int.mjs.map +1 -0
  159. package/dist/number/branded-types/non-zero-int16.d.mts +166 -0
  160. package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -0
  161. package/dist/number/branded-types/non-zero-int16.mjs +145 -0
  162. package/dist/number/branded-types/non-zero-int16.mjs.map +1 -0
  163. package/dist/number/branded-types/non-zero-int32.d.mts +158 -0
  164. package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -0
  165. package/dist/number/branded-types/non-zero-int32.mjs +145 -0
  166. package/dist/number/branded-types/non-zero-int32.mjs.map +1 -0
  167. package/dist/number/branded-types/non-zero-safe-int.d.mts +148 -0
  168. package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -0
  169. package/dist/number/branded-types/non-zero-safe-int.mjs +145 -0
  170. package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -0
  171. package/dist/number/branded-types/non-zero-uint16.d.mts +160 -0
  172. package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -0
  173. package/dist/number/branded-types/non-zero-uint16.mjs +140 -0
  174. package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -0
  175. package/dist/number/branded-types/non-zero-uint32.d.mts +156 -0
  176. package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -0
  177. package/dist/number/branded-types/non-zero-uint32.mjs +140 -0
  178. package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -0
  179. package/dist/number/branded-types/positive-finite-number.d.mts +171 -0
  180. package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -0
  181. package/dist/number/branded-types/positive-finite-number.mjs +165 -0
  182. package/dist/number/branded-types/positive-finite-number.mjs.map +1 -0
  183. package/dist/number/branded-types/positive-int.d.mts +270 -0
  184. package/dist/number/branded-types/positive-int.d.mts.map +1 -0
  185. package/dist/number/branded-types/positive-int.mjs +257 -0
  186. package/dist/number/branded-types/positive-int.mjs.map +1 -0
  187. package/dist/number/branded-types/positive-int16.d.mts +162 -0
  188. package/dist/number/branded-types/positive-int16.d.mts.map +1 -0
  189. package/dist/number/branded-types/positive-int16.mjs +139 -0
  190. package/dist/number/branded-types/positive-int16.mjs.map +1 -0
  191. package/dist/number/branded-types/positive-int32.d.mts +158 -0
  192. package/dist/number/branded-types/positive-int32.d.mts.map +1 -0
  193. package/dist/number/branded-types/positive-int32.mjs +139 -0
  194. package/dist/number/branded-types/positive-int32.mjs.map +1 -0
  195. package/dist/number/branded-types/positive-safe-int.d.mts +152 -0
  196. package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -0
  197. package/dist/number/branded-types/positive-safe-int.mjs +138 -0
  198. package/dist/number/branded-types/positive-safe-int.mjs.map +1 -0
  199. package/dist/number/branded-types/positive-uint16.d.mts +160 -0
  200. package/dist/number/branded-types/positive-uint16.d.mts.map +1 -0
  201. package/dist/number/branded-types/positive-uint16.mjs +139 -0
  202. package/dist/number/branded-types/positive-uint16.mjs.map +1 -0
  203. package/dist/number/branded-types/positive-uint32.d.mts +156 -0
  204. package/dist/number/branded-types/positive-uint32.d.mts.map +1 -0
  205. package/dist/number/branded-types/positive-uint32.mjs +139 -0
  206. package/dist/number/branded-types/positive-uint32.mjs.map +1 -0
  207. package/dist/number/branded-types/safe-int.d.mts +243 -0
  208. package/dist/number/branded-types/safe-int.d.mts.map +1 -0
  209. package/dist/number/branded-types/safe-int.mjs +240 -0
  210. package/dist/number/branded-types/safe-int.mjs.map +1 -0
  211. package/dist/number/branded-types/safe-uint.d.mts +151 -0
  212. package/dist/number/branded-types/safe-uint.d.mts.map +1 -0
  213. package/dist/number/branded-types/safe-uint.mjs +138 -0
  214. package/dist/number/branded-types/safe-uint.mjs.map +1 -0
  215. package/dist/number/branded-types/uint.d.mts +144 -0
  216. package/dist/number/branded-types/uint.d.mts.map +1 -0
  217. package/dist/number/branded-types/uint.mjs +132 -0
  218. package/dist/number/branded-types/uint.mjs.map +1 -0
  219. package/dist/number/branded-types/uint16.d.mts +157 -0
  220. package/dist/number/branded-types/uint16.d.mts.map +1 -0
  221. package/dist/number/branded-types/uint16.mjs +137 -0
  222. package/dist/number/branded-types/uint16.mjs.map +1 -0
  223. package/dist/number/branded-types/uint32.d.mts +185 -0
  224. package/dist/number/branded-types/uint32.d.mts.map +1 -0
  225. package/dist/number/branded-types/uint32.mjs +169 -0
  226. package/dist/number/branded-types/uint32.mjs.map +1 -0
  227. package/dist/number/enum/index.d.mts +3 -0
  228. package/dist/number/enum/index.d.mts.map +1 -0
  229. package/dist/number/enum/index.mjs +3 -0
  230. package/dist/number/enum/index.mjs.map +1 -0
  231. package/dist/number/enum/int8.d.mts +202 -0
  232. package/dist/number/enum/int8.d.mts.map +1 -0
  233. package/dist/number/enum/int8.mjs +296 -0
  234. package/dist/number/enum/int8.mjs.map +1 -0
  235. package/dist/number/enum/uint8.d.mts +128 -0
  236. package/dist/number/enum/uint8.d.mts.map +1 -0
  237. package/dist/number/enum/uint8.mjs +251 -0
  238. package/dist/number/enum/uint8.mjs.map +1 -0
  239. package/dist/number/index.d.mts +5 -0
  240. package/dist/number/index.d.mts.map +1 -0
  241. package/dist/number/index.mjs +31 -0
  242. package/dist/number/index.mjs.map +1 -0
  243. package/dist/number/num.d.mts +515 -0
  244. package/dist/number/num.d.mts.map +1 -0
  245. package/dist/number/num.mjs +513 -0
  246. package/dist/number/num.mjs.map +1 -0
  247. package/dist/number/refined-number-utils.d.mts +191 -0
  248. package/dist/number/refined-number-utils.d.mts.map +1 -0
  249. package/dist/number/refined-number-utils.mjs +179 -0
  250. package/dist/number/refined-number-utils.mjs.map +1 -0
  251. package/dist/object/index.d.mts +2 -0
  252. package/dist/object/index.d.mts.map +1 -0
  253. package/dist/object/index.mjs +2 -0
  254. package/dist/object/index.mjs.map +1 -0
  255. package/dist/object/object.d.mts +296 -0
  256. package/dist/object/object.d.mts.map +1 -0
  257. package/dist/object/object.mjs +295 -0
  258. package/dist/object/object.mjs.map +1 -0
  259. package/dist/others/cast-mutable.d.mts +110 -0
  260. package/dist/others/cast-mutable.d.mts.map +1 -0
  261. package/dist/others/cast-mutable.mjs +114 -0
  262. package/dist/others/cast-mutable.mjs.map +1 -0
  263. package/dist/others/cast-readonly.d.mts +189 -0
  264. package/dist/others/cast-readonly.d.mts.map +1 -0
  265. package/dist/others/cast-readonly.mjs +193 -0
  266. package/dist/others/cast-readonly.mjs.map +1 -0
  267. package/dist/others/if-then.d.mts +98 -0
  268. package/dist/others/if-then.d.mts.map +1 -0
  269. package/dist/others/if-then.mjs +100 -0
  270. package/dist/others/if-then.mjs.map +1 -0
  271. package/dist/others/index.d.mts +8 -0
  272. package/dist/others/index.d.mts.map +1 -0
  273. package/dist/others/index.mjs +8 -0
  274. package/dist/others/index.mjs.map +1 -0
  275. package/dist/others/map-nullable.d.mts +151 -0
  276. package/dist/others/map-nullable.d.mts.map +1 -0
  277. package/dist/others/map-nullable.mjs +159 -0
  278. package/dist/others/map-nullable.mjs.map +1 -0
  279. package/dist/others/memoize-function.d.mts +173 -0
  280. package/dist/others/memoize-function.d.mts.map +1 -0
  281. package/dist/others/memoize-function.mjs +189 -0
  282. package/dist/others/memoize-function.mjs.map +1 -0
  283. package/dist/others/tuple.d.mts +159 -0
  284. package/dist/others/tuple.d.mts.map +1 -0
  285. package/dist/others/tuple.mjs +161 -0
  286. package/dist/others/tuple.mjs.map +1 -0
  287. package/dist/others/unknown-to-string.d.mts +180 -0
  288. package/dist/others/unknown-to-string.d.mts.map +1 -0
  289. package/dist/others/unknown-to-string.mjs +211 -0
  290. package/dist/others/unknown-to-string.mjs.map +1 -0
  291. package/dist/tsconfig.json +1 -0
  292. package/package.json +16 -14
@@ -0,0 +1,2915 @@
1
+ import '../collections/imap-mapped.mjs';
2
+ import { IMap } from '../collections/imap.mjs';
3
+ import '../collections/iset-mapped.mjs';
4
+ import '../collections/iset.mjs';
5
+ import './tuple-utils.mjs';
6
+ import { isString, isUndefined } from '../guard/is-type.mjs';
7
+ import { Optional } from '../functional/optional.mjs';
8
+ import { pipe } from '../functional/pipe.mjs';
9
+ import { Result } from '../functional/result.mjs';
10
+ import '../number/branded-types/finite-number.mjs';
11
+ import '../number/branded-types/int.mjs';
12
+ import '../number/branded-types/int16.mjs';
13
+ import '../number/branded-types/int32.mjs';
14
+ import '../number/branded-types/non-negative-finite-number.mjs';
15
+ import '../number/branded-types/non-negative-int16.mjs';
16
+ import '../number/branded-types/non-negative-int32.mjs';
17
+ import '../number/branded-types/non-zero-finite-number.mjs';
18
+ import '../number/branded-types/non-zero-int.mjs';
19
+ import '../number/branded-types/non-zero-int16.mjs';
20
+ import '../number/branded-types/non-zero-int32.mjs';
21
+ import '../number/branded-types/non-zero-safe-int.mjs';
22
+ import '../number/branded-types/non-zero-uint16.mjs';
23
+ import '../number/branded-types/non-zero-uint32.mjs';
24
+ import '../number/branded-types/positive-finite-number.mjs';
25
+ import '../number/branded-types/positive-int.mjs';
26
+ import '../number/branded-types/positive-int16.mjs';
27
+ import '../number/branded-types/positive-int32.mjs';
28
+ import '../number/branded-types/positive-safe-int.mjs';
29
+ import '../number/branded-types/positive-uint16.mjs';
30
+ import { asPositiveUint32 } from '../number/branded-types/positive-uint32.mjs';
31
+ import '../number/branded-types/safe-int.mjs';
32
+ import '../number/branded-types/safe-uint.mjs';
33
+ import '../number/branded-types/uint.mjs';
34
+ import '../number/branded-types/uint16.mjs';
35
+ import { asUint32, Uint32 } from '../number/branded-types/uint32.mjs';
36
+ import '../number/enum/int8.mjs';
37
+ import '../number/enum/uint8.mjs';
38
+ import { Num } from '../number/num.mjs';
39
+ import '../number/refined-number-utils.mjs';
40
+ import { castMutable } from '../others/cast-mutable.mjs';
41
+ import { tp } from '../others/tuple.mjs';
42
+ import { unknownToString } from '../others/unknown-to-string.mjs';
43
+ import { range } from '../iterator/range.mjs';
44
+
45
+ /**
46
+ * A comprehensive, immutable utility library for array manipulations in TypeScript.
47
+ * Provides a wide range of functions for array creation, validation, transformation,
48
+ * reduction, slicing, set operations, and more, with a focus on type safety and
49
+ * leveraging TypeScript's type inference capabilities.
50
+ * All functions operate on `readonly` arrays and return new `readonly` arrays,
51
+ * ensuring immutability.
52
+ */
53
+ var Arr;
54
+ (function (Arr) {
55
+ /**
56
+ * Returns the size (length) of an array as a type-safe branded integer.
57
+ *
58
+ * This function provides the array length with enhanced type safety through branded types:
59
+ * - For arrays known to be non-empty at compile time: returns `PositiveNumber & SizeType.Arr`
60
+ * - For general arrays that may be empty: returns `SizeType.Arr` (branded Uint32)
61
+ *
62
+ * The returned value is always a non-negative integer that can be safely used for array indexing
63
+ * and size comparisons. The branded type prevents common integer overflow issues and provides
64
+ * better type checking than plain numbers.
65
+ *
66
+ * @template Ar The exact type of the input array, used for precise return type inference.
67
+ * @param array The array to measure. Can be any readonly array type.
68
+ * @returns The length of the array as a branded type:
69
+ * - `IntersectBrand<PositiveNumber, SizeType.Arr>` for known non-empty arrays
70
+ * - `SizeType.Arr` for general arrays (branded Uint32, may be 0)
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Known non-empty arrays get positive branded type
75
+ * const tuple = [1, 2, 3] as const;
76
+ * const tupleSize = Arr.size(tuple);
77
+ * // Type: IntersectBrand<PositiveNumber, SizeType.Arr>
78
+ * // Value: 3 (branded, guaranteed positive)
79
+ *
80
+ * const nonEmpty: NonEmptyArray<string> = ['a', 'b'] as NonEmptyArray<string>;
81
+ * const nonEmptySize = Arr.size(nonEmpty);
82
+ * // Type: IntersectBrand<PositiveNumber, SizeType.Arr>
83
+ * // Guaranteed to be > 0
84
+ *
85
+ * // General arrays may be empty, get regular branded type
86
+ * const generalArray: number[] = [1, 2, 3];
87
+ * const generalSize = Arr.size(generalArray);
88
+ * // Type: SizeType.Arr (branded Uint32)
89
+ * // May be 0 or positive
90
+ *
91
+ * // Empty arrays
92
+ * const emptyArray = [] as const;
93
+ * const emptySize = Arr.size(emptyArray);
94
+ * // Type: SizeType.Arr
95
+ * // Value: 0 (branded)
96
+ *
97
+ * // Runtime arrays with unknown content
98
+ * const dynamicArray = Array.from({ length: Math.random() * 10 }, (_, i) => i);
99
+ * const dynamicSize = Arr.size(dynamicArray);
100
+ * // Type: SizeType.Arr (may be 0)
101
+ *
102
+ * // Using size for safe operations
103
+ * const data = [10, 20, 30];
104
+ * const dataSize = Arr.size(data);
105
+ *
106
+ * // Safe for array creation
107
+ * const indices = Arr.seq(dataSize); // Creates [0, 1, 2]
108
+ * const zeros = Arr.zeros(dataSize); // Creates [0, 0, 0]
109
+ *
110
+ * // Safe for bounds checking
111
+ * const isValidIndex = (index: number) => index >= 0 && index < dataSize;
112
+ *
113
+ * // Comparison with other sizes
114
+ * const otherArray = ['a', 'b'];
115
+ * const sizeDiff = Uint32.sub(Arr.size(data), Arr.size(otherArray)); // 1
116
+ *
117
+ * // Functional composition
118
+ * const arrays = [
119
+ * [1, 2],
120
+ * [3, 4, 5],
121
+ * [],
122
+ * [6]
123
+ * ];
124
+ * const sizes = arrays.map(Arr.size); // [2, 3, 0, 1] (all branded)
125
+ * const totalElements = sizes.reduce(Uint32.add, 0); // 6
126
+ *
127
+ * // Type guards work with size
128
+ * if (Arr.size(data) > 0) {
129
+ * // TypeScript knows data is non-empty here
130
+ * const firstElement = data[0]; // Safe access
131
+ * }
132
+ *
133
+ * // Type inference examples
134
+ * expectType<typeof tupleSize, IntersectBrand<PositiveNumber, SizeType.Arr>>('=');
135
+ * expectType<typeof generalSize, SizeType.Arr>('=');
136
+ * expectType<typeof emptySize, SizeType.Arr>('=');
137
+ * ```
138
+ *
139
+ * @see {@link length} - Alias for this function
140
+ * @see {@link isEmpty} for checking if size is 0
141
+ * @see {@link isNonEmpty} for checking if size > 0
142
+ */
143
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
144
+ Arr.size = ((array) => asUint32(array.length));
145
+ Arr.length = Arr.size;
146
+ // type guard
147
+ /**
148
+ * Type guard that checks if a value is an array, excluding types that cannot be arrays.
149
+ * This function refines the type by filtering out non-array types from unions.
150
+ * @template E The input type that may or may not be an array.
151
+ * @param value The value to check.
152
+ * @returns `true` if the value is an array, `false` otherwise.
153
+ * @example
154
+ * ```ts
155
+ * function processValue(value: string | number[] | null) {
156
+ * if (Arr.isArray(value)) {
157
+ * // value is now typed as number[]
158
+ * console.log(value.length);
159
+ * }
160
+ * }
161
+ *
162
+ * Arr.isArray([1, 2, 3]); // true
163
+ * Arr.isArray("hello"); // false
164
+ * Arr.isArray(null); // false
165
+ * ```
166
+ */
167
+ Arr.isArray = (value) => Array.isArray(value);
168
+ // validation
169
+ /**
170
+ * Type guard that checks if an array is empty (has no elements).
171
+ *
172
+ * This function serves as both a runtime check and a TypeScript type guard,
173
+ * narrowing the array type to `readonly []` when the check passes. It's useful
174
+ * for conditional logic and type-safe handling of potentially empty arrays.
175
+ *
176
+ * @template E The type of elements in the array.
177
+ * @param array The array to check for emptiness.
178
+ * @returns `true` if the array has length 0, `false` otherwise.
179
+ * When `true`, TypeScript narrows the type to `readonly []`.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // Basic emptiness checking
184
+ * const emptyArray: number[] = [];
185
+ * const nonEmptyArray = [1, 2, 3];
186
+ *
187
+ * console.log(Arr.isEmpty(emptyArray)); // true
188
+ * console.log(Arr.isEmpty(nonEmptyArray)); // false
189
+ *
190
+ * // Type guard behavior
191
+ * function processArray(arr: readonly number[]) {
192
+ * if (Arr.isEmpty(arr)) {
193
+ * // arr is now typed as readonly []
194
+ * console.log('Array is empty');
195
+ * return 0;
196
+ * } else {
197
+ * // arr is now typed as NonEmptyArray<number>
198
+ * return arr[0]; // Safe access - TypeScript knows it's non-empty
199
+ * }
200
+ * }
201
+ *
202
+ * // Conditional processing
203
+ * const data = [10, 20, 30];
204
+ * if (!Arr.isEmpty(data)) {
205
+ * // Safe to access elements
206
+ * const firstElement = data[0]; // No undefined risk
207
+ * const lastElement = data[data.length - 1];
208
+ * }
209
+ *
210
+ * // Filtering empty arrays
211
+ * const arrayList: readonly number[][] = [[1, 2], [], [3], []];
212
+ * const nonEmptyArrays = arrayList.filter(arr => !Arr.isEmpty(arr));
213
+ * // nonEmptyArrays: [[1, 2], [3]]
214
+ *
215
+ * // Early returns
216
+ * function sumArray(numbers: readonly number[]): number {
217
+ * if (Arr.isEmpty(numbers)) {
218
+ * return 0; // Handle empty case early
219
+ * }
220
+ * return numbers.reduce((sum, n) => sum + n, 0);
221
+ * }
222
+ *
223
+ * // Type inference examples
224
+ * const testEmpty = [] as const;
225
+ * const testNonEmpty = [1, 2] as const;
226
+ *
227
+ * expectType<Parameters<typeof Arr.isEmpty>[0], readonly unknown[]>('=');
228
+ * expectType<ReturnType<typeof Arr.isEmpty>, boolean>('=');
229
+ * ```
230
+ *
231
+ * @see {@link isNonEmpty} for the opposite check (non-empty arrays)
232
+ * @see {@link size} for getting the exact length
233
+ * @see {@link isArrayOfLength} for checking specific lengths
234
+ */
235
+ Arr.isEmpty = (array) => array.length === 0;
236
+ /**
237
+ * Type guard that checks if an array is non-empty (has at least one element).
238
+ *
239
+ * This function serves as both a runtime check and a TypeScript type guard,
240
+ * narrowing the array type to `NonEmptyArray<E>` when the check passes. This enables
241
+ * safe access to array elements without undefined checks, as TypeScript knows the array
242
+ * has at least one element.
243
+ *
244
+ * @template E The type of elements in the array.
245
+ * @param array The array to check for non-emptiness.
246
+ * @returns `true` if the array has length > 0, `false` otherwise.
247
+ * When `true`, TypeScript narrows the type to `NonEmptyArray<E>`.
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * // Basic non-emptiness checking
252
+ * const emptyArray: number[] = [];
253
+ * const nonEmptyArray = [1, 2, 3];
254
+ *
255
+ * console.log(Arr.isNonEmpty(emptyArray)); // false
256
+ * console.log(Arr.isNonEmpty(nonEmptyArray)); // true
257
+ *
258
+ * // Type guard behavior enables safe element access
259
+ * function getFirstElement(arr: readonly number[]): number | undefined {
260
+ * if (Arr.isNonEmpty(arr)) {
261
+ * // arr is now typed as NonEmptyArray<number>
262
+ * return arr[0]; // Safe - no undefined, TypeScript knows this exists
263
+ * }
264
+ * return undefined;
265
+ * }
266
+ *
267
+ * // Safe operations on non-empty arrays
268
+ * function processData(data: readonly string[]) {
269
+ * if (Arr.isNonEmpty(data)) {
270
+ * // All of these are now safe without undefined checks
271
+ * const first = data[0];
272
+ * const last = data[data.length - 1];
273
+ * const middle = data[Math.floor(data.length / 2)];
274
+ *
275
+ * // Can safely use non-empty array methods
276
+ * const joined = data.join(', ');
277
+ * const reduced = data.reduce((acc, item) => acc + item.length, 0);
278
+ * }
279
+ * }
280
+ *
281
+ * // Filtering and working with arrays
282
+ * const possiblyEmptyArrays: readonly number[][] = [
283
+ * [1, 2, 3],
284
+ * [],
285
+ * [4, 5],
286
+ * []
287
+ * ];
288
+ *
289
+ * // Get only non-empty arrays with proper typing
290
+ * const definitelyNonEmpty = possiblyEmptyArrays.filter(Arr.isNonEmpty);
291
+ * // Type: NonEmptyArray<number>[]
292
+ *
293
+ * // Now safe to access elements
294
+ * const firstElements = definitelyNonEmpty.map(arr => arr[0]); // [1, 4]
295
+ *
296
+ * // Early validation
297
+ * function calculateAverage(numbers: readonly number[]): number {
298
+ * if (!Arr.isNonEmpty(numbers)) {
299
+ * throw new Error('Cannot calculate average of empty array');
300
+ * }
301
+ *
302
+ * // numbers is now NonEmptyArray<number>
303
+ * return numbers.reduce((sum, n) => sum + n, 0) / numbers.length;
304
+ * }
305
+ *
306
+ * // Functional composition
307
+ * const arrayGroups = [
308
+ * [1, 2],
309
+ * [],
310
+ * [3, 4, 5],
311
+ * []
312
+ * ];
313
+ *
314
+ * const nonEmptyGroups = arrayGroups
315
+ * .filter(Arr.isNonEmpty) // Filter to NonEmptyArray<number>[]
316
+ * .map(group => group[0]); // Safe access to first element: [1, 3]
317
+ *
318
+ * // Combined with other array operations
319
+ * function processArraySafely<T>(
320
+ * arr: readonly T[],
321
+ * processor: (item: T) => string
322
+ * ): string {
323
+ * if (Arr.isNonEmpty(arr)) {
324
+ * return arr.map(processor).join(' -> ');
325
+ * }
326
+ * return 'No items to process';
327
+ * }
328
+ *
329
+ * // Type inference examples
330
+ * const testArray = [1, 2, 3];
331
+ * const isNonEmptyResult = Arr.isNonEmpty(testArray);
332
+ *
333
+ * expectType<typeof isNonEmptyResult, boolean>('=');
334
+ * expectType<Parameters<typeof Arr.isNonEmpty>[0], readonly unknown[]>('=');
335
+ *
336
+ * // Type narrowing in conditional
337
+ * if (Arr.isNonEmpty(testArray)) {
338
+ * expectType<typeof testArray, NonEmptyArray<number>>('=');
339
+ * }
340
+ * ```
341
+ *
342
+ * @see {@link isEmpty} for the opposite check (empty arrays)
343
+ * @see {@link size} for getting the exact length
344
+ * @see {@link head} for safely getting the first element
345
+ * @see {@link last} for safely getting the last element
346
+ */
347
+ Arr.isNonEmpty = (array) => array.length > 0;
348
+ /**
349
+ * Checks if an array has a specific length.
350
+ * @template E The type of elements in the array.
351
+ * @template N The expected length of the array (must be a number type).
352
+ * @param array The array to check.
353
+ * @param len The expected length.
354
+ * @returns `true` if the array has the specified length, `false` otherwise.
355
+ * @example
356
+ * ```ts
357
+ * const arr: readonly number[] = [1, 2, 3];
358
+ * if (Arr.isArrayOfLength(arr, 3)) {
359
+ * // arr is now typed as readonly [number, number, number]
360
+ * }
361
+ * Arr.isArrayOfLength([1, 2], 3); // false
362
+ * ```
363
+ */
364
+ Arr.isArrayOfLength = (array, len) => array.length === len;
365
+ /**
366
+ * Checks if an array has at least a specific length.
367
+ * @template E The type of elements in the array.
368
+ * @template N The minimum expected length of the array (must be a number type).
369
+ * @param array The array to check.
370
+ * @param len The minimum expected length.
371
+ * @returns `true` if the array has at least the specified length, `false` otherwise.
372
+ * @example
373
+ * ```ts
374
+ * const arr: readonly number[] = [1, 2, 3];
375
+ * if (Arr.isArrayAtLeastLength(arr, 2)) {
376
+ * // arr is now typed as readonly [number, number, ...number[]]
377
+ * }
378
+ * Arr.isArrayAtLeastLength([1], 2); // false
379
+ * ```
380
+ */
381
+ Arr.isArrayAtLeastLength = (array, len) => array.length >= len;
382
+ /**
383
+ * Checks if an index is within the valid range of an array (i.e., `0 <= index < array.length`).
384
+ * @template E The type of elements in the array.
385
+ * @param array The input array.
386
+ * @param index The index to check.
387
+ * @returns `true` if the index is within the array bounds, `false` otherwise.
388
+ * @example
389
+ * ```ts
390
+ * Arr.indexIsInRange([10, 20], 0); // true
391
+ * Arr.indexIsInRange([10, 20], 1); // true
392
+ * Arr.indexIsInRange([10, 20], 2); // false
393
+ * Arr.indexIsInRange([10, 20], -1); // false
394
+ * Arr.indexIsInRange([], 0); // false
395
+ * ```
396
+ */
397
+ Arr.indexIsInRange = (array, index) => Num.isInRange(0, array.length)(index);
398
+ // array creation
399
+ /**
400
+ * Creates an array of zeros with the specified length.
401
+ *
402
+ * This function provides compile-time type safety with precise return types:
403
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple with exact length
404
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<0>`
405
+ * - Otherwise, returns a `readonly 0[]` that may be empty
406
+ *
407
+ * @template N The type of the length parameter. When a `SmallUint` literal is provided,
408
+ * the return type will be a tuple of exactly that length filled with zeros.
409
+ * @param len The length of the array to create. Must be a non-negative integer.
410
+ * @returns An immutable array of zeros. The exact return type depends on the input:
411
+ * - `ArrayOfLength<N, 0>` when `N` is a `SmallUint` literal
412
+ * - `NonEmptyArray<0>` when `len` is a positive runtime value
413
+ * - `readonly 0[]` for general non-negative values
414
+ *
415
+ * @example
416
+ * ```typescript
417
+ * // Compile-time known lengths produce precise tuple types
418
+ * const exactLength = Arr.zeros(3); // readonly [0, 0, 0]
419
+ * const empty = Arr.zeros(0); // readonly []
420
+ *
421
+ * // Runtime positive values produce non-empty arrays
422
+ * const count = Math.floor(Math.random() * 5) + 1;
423
+ * const nonEmpty = Arr.zeros(count); // NonEmptyArray<0>
424
+ *
425
+ * // General runtime values may be empty
426
+ * const maybeEmpty = Arr.zeros(Math.floor(Math.random() * 5)); // readonly 0[]
427
+ *
428
+ * // Type inference examples
429
+ * expectType<typeof exactLength, readonly [0, 0, 0]>('=');
430
+ * expectType<typeof empty, readonly []>('=');
431
+ * expectType<typeof nonEmpty, NonEmptyArray<0>>('=');
432
+ * expectType<typeof maybeEmpty, readonly 0[]>('=');
433
+ * ```
434
+ */
435
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
436
+ Arr.zeros = ((len) => Array.from({ length: len }).fill(0));
437
+ /**
438
+ * Creates a sequence of consecutive integers from 0 to `len-1`.
439
+ *
440
+ * This function generates index sequences with precise compile-time typing:
441
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple of consecutive integers
442
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<SizeType.Arr>`
443
+ * - Otherwise, returns a `readonly SizeType.Arr[]` that may be empty
444
+ *
445
+ * @template N The type of the length parameter. When a `SmallUint` literal is provided,
446
+ * the return type will be a tuple containing the sequence [0, 1, 2, ..., N-1].
447
+ * @param len The length of the sequence to create. Must be a non-negative integer.
448
+ * @returns An immutable array containing the sequence [0, 1, 2, ..., len-1].
449
+ * The exact return type depends on the input:
450
+ * - `Seq<N>` (precise tuple) when `N` is a `SmallUint` literal
451
+ * - `NonEmptyArray<SizeType.Arr>` when `len` is a positive runtime value
452
+ * - `readonly SizeType.Arr[]` for general non-negative values
453
+ *
454
+ * @example
455
+ * ```typescript
456
+ * // Compile-time known lengths produce precise tuple types
457
+ * const indices = Arr.seq(4); // readonly [0, 1, 2, 3]
458
+ * const empty = Arr.seq(0); // readonly []
459
+ * const single = Arr.seq(1); // readonly [0]
460
+ *
461
+ * // Runtime positive values produce non-empty arrays
462
+ * const count = Math.floor(Math.random() * 5) + 1;
463
+ * const nonEmpty = Arr.seq(count); // NonEmptyArray<SizeType.Arr>
464
+ *
465
+ * // General runtime values may be empty
466
+ * const maybeEmpty = Arr.seq(Math.floor(Math.random() * 5)); // readonly SizeType.Arr[]
467
+ *
468
+ * // Useful for generating array indices
469
+ * const data = ['a', 'b', 'c', 'd'];
470
+ * const indexSequence = Arr.seq(data.length); // [0, 1, 2, 3]
471
+ *
472
+ * // Type inference examples
473
+ * expectType<typeof indices, readonly [0, 1, 2, 3]>('=');
474
+ * expectType<typeof empty, readonly []>('=');
475
+ * expectType<typeof single, readonly [0]>('=');
476
+ * ```
477
+ */
478
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
479
+ Arr.seq = ((len) => Array.from({ length: len }, (_, i) => asUint32(i)));
480
+ /**
481
+ * Creates a new array of the specified length, with each position filled with the provided initial value.
482
+ *
483
+ * This function provides compile-time type safety with precise return types and performs shallow copying
484
+ * of the initial value (the same reference is used for all positions):
485
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple of exactly that length
486
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<V>`
487
+ * - Otherwise, returns a `readonly V[]` that may be empty
488
+ *
489
+ * @template V The type of the initial value. The `const` constraint preserves literal types.
490
+ * @template N The type of the length parameter when it's a `SmallUint` literal.
491
+ * @param len The length of the array to create. Must be a non-negative integer.
492
+ * @param init The value to fill each position with. The same reference is used for all positions.
493
+ * @returns An immutable array filled with the initial value. The exact return type depends on the length:
494
+ * - `ArrayOfLength<N, V>` when `len` is a `SmallUint` literal
495
+ * - `NonEmptyArray<V>` when `len` is a positive runtime value
496
+ * - `readonly V[]` for general non-negative values
497
+ *
498
+ * @example
499
+ * ```typescript
500
+ * // Compile-time known lengths produce precise tuple types
501
+ * const strings = Arr.create(3, 'hello'); // readonly ['hello', 'hello', 'hello']
502
+ * const numbers = Arr.create(2, 42); // readonly [42, 42]
503
+ * const empty = Arr.create(0, 'unused'); // readonly []
504
+ *
505
+ * // Object references are shared (shallow copy behavior)
506
+ * const obj = { id: 1, name: 'test' };
507
+ * const objects = Arr.create(3, obj); // readonly [obj, obj, obj]
508
+ * objects[0] === objects[1]; // true - same reference
509
+ * objects[0].id = 999; // Mutates the shared object
510
+ * console.log(objects[1].id); // 999 - all positions affected
511
+ *
512
+ * // Runtime positive values produce non-empty arrays
513
+ * const count = Math.floor(Math.random() * 5) + 1;
514
+ * const nonEmpty = Arr.create(count, 'item'); // NonEmptyArray<string>
515
+ *
516
+ * // Literal type preservation with const assertion
517
+ * const literals = Arr.create(2, 'success' as const); // readonly ['success', 'success']
518
+ *
519
+ * // Type inference examples
520
+ * expectType<typeof strings, readonly ['hello', 'hello', 'hello']>('=');
521
+ * expectType<typeof numbers, readonly [42, 42]>('=');
522
+ * expectType<typeof empty, readonly []>('=');
523
+ * ```
524
+ *
525
+ * @see {@link zeros} for creating arrays filled with zeros
526
+ * @see {@link seq} for creating sequences of consecutive integers
527
+ */
528
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
529
+ Arr.create = ((len, init) => Array.from({ length: Math.max(0, len) }, () => init));
530
+ Arr.newArray = Arr.create;
531
+ /**
532
+ * Creates a shallow copy of an array, preserving the exact type signature.
533
+ *
534
+ * This function creates a new array with the same elements as the input, but with a new array reference.
535
+ * Object references within the array are preserved (shallow copy), and the readonly/mutable status
536
+ * of the array type is maintained.
537
+ *
538
+ * @template Ar The exact type of the input array, preserving tuple types, readonly status, and element types.
539
+ * @param array The array to copy. Can be any array type: mutable, readonly, tuple, or general array.
540
+ * @returns A new array that is a shallow copy of the input. The return type exactly matches the input type,
541
+ * preserving readonly status, tuple structure, and element types.
542
+ *
543
+ * @example
544
+ * ```typescript
545
+ * // Mutable arrays remain mutable
546
+ * const mutableOriginal = [1, 2, 3];
547
+ * const mutableCopy = Arr.copy(mutableOriginal); // number[]
548
+ * mutableCopy[0] = 999; // OK - still mutable
549
+ * mutableOriginal[0]; // 1 - original unchanged
550
+ *
551
+ * // Readonly arrays remain readonly
552
+ * const readonlyOriginal = [1, 2, 3] as const;
553
+ * const readonlyCopy = Arr.copy(readonlyOriginal); // readonly [1, 2, 3]
554
+ * // readonlyCopy[0] = 999; // Error - readonly array
555
+ *
556
+ * // Tuple types are preserved
557
+ * const tupleOriginal: [string, number, boolean] = ['hello', 42, true];
558
+ * const tupleCopy = Arr.copy(tupleOriginal); // [string, number, boolean]
559
+ *
560
+ * // Shallow copy behavior with objects
561
+ * const objectArray = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
562
+ * const objectCopy = Arr.copy(objectArray);
563
+ * objectCopy[0].name = 'Charlie'; // Mutates the shared object reference
564
+ * console.log(objectArray[0].name); // 'Charlie' - original affected
565
+ * objectCopy.push({ id: 3, name: 'Dave' }); // Array structure changes don't affect original
566
+ * console.log(objectArray.length); // 2 - original array length unchanged
567
+ *
568
+ * // Empty arrays
569
+ * const emptyArray: number[] = [];
570
+ * const emptyCopy = Arr.copy(emptyArray); // number[]
571
+ * const emptyTuple = [] as const;
572
+ * const emptyTupleCopy = Arr.copy(emptyTuple); // readonly []
573
+ *
574
+ * // Type inference examples
575
+ * expectType<typeof mutableCopy, number[]>('=');
576
+ * expectType<typeof readonlyCopy, readonly [1, 2, 3]>('=');
577
+ * expectType<typeof tupleCopy, [string, number, boolean]>('=');
578
+ * ```
579
+ *
580
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice | Array.prototype.slice}
581
+ * The underlying implementation uses `slice()` for efficient shallow copying
582
+ */
583
+ Arr.copy = (array) =>
584
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
585
+ array.slice();
586
+ /**
587
+ * Creates an array of numbers within a specified range with optional step increment.
588
+ *
589
+ * This function generates arithmetic sequences with advanced compile-time type inference:
590
+ * - When `start` and `end` are {@link SmallUint} literals and `step` is 1 (or omitted), returns a precise tuple type
591
+ * - When parameters are runtime values, returns appropriate array types based on sign constraints
592
+ * - Empty arrays are returned for invalid ranges (e.g., start ≥ end with positive step)
593
+ * - Never throws exceptions - invalid parameters result in empty arrays
594
+ *
595
+ * **SmallUint Constraint:** The {@link SmallUint} constraint (0-255) enables precise tuple type inference
596
+ * for compile-time known ranges. This allows TypeScript to compute exact tuple types like `readonly [1, 2, 3, 4]`
597
+ * instead of generic `readonly number[]`.
598
+ *
599
+ * **Type Inference Behavior:**
600
+ * - Literal {@link SmallUint} values with step=1 → precise tuple type (`RangeList<S, E>`)
601
+ * - Non-negative parameters → `readonly SafeUint[]`
602
+ * - Mixed signs or negative parameters → `readonly SafeInt[]`
603
+ * - Runtime values → lose precise typing but maintain safety
604
+ *
605
+ * @template S The type of the start value. When a {@link SmallUint} literal (0-255), enables precise tuple typing.
606
+ * @template E The type of the end value. When a {@link SmallUint} literal (0-255), enables precise tuple typing.
607
+ * @param start The start of the range (inclusive). Must be a safe integer. Supports:
608
+ * - **Literal {@link SmallUint}:** Enables precise tuple types (0-255)
609
+ * - **Runtime {@link SafeInt}:** Fallback to general array types
610
+ * - **Negative values:** Supported for countdown sequences
611
+ * @param end The end of the range (exclusive). Must be a safe integer. Supports:
612
+ * - **Literal {@link SmallUint}:** Enables precise tuple types (0-255)
613
+ * - **Runtime {@link SafeInt}:** Fallback to general array types
614
+ * - **Equal to start:** Results in empty array
615
+ * @param step The step increment (default: 1). Must be a non-zero safe integer.
616
+ * - **Positive step:** generates increasing sequence from start to end
617
+ * - **Negative step:** generates decreasing sequence from start to end
618
+ * - **Zero step:** Not allowed (branded type prevents this)
619
+ * @returns An immutable array containing the arithmetic sequence. Return type depends on parameters:
620
+ * - `RangeList<S, E>` (precise tuple like `readonly [1, 2, 3, 4]`) when `S` and `E` are {@link SmallUint} literals and step is 1
621
+ * - `readonly SafeUint[]` when all parameters are non-negative
622
+ * - `readonly SafeInt[]` for general integer ranges including negative values
623
+ *
624
+ * @example
625
+ * ```typescript
626
+ * // Compile-time known ranges with step=1 produce precise tuple types
627
+ * const range1to4 = Arr.range(1, 5); // readonly [1, 2, 3, 4]
628
+ * const range0to2 = Arr.range(0, 3); // readonly [0, 1, 2]
629
+ * const emptyRange = Arr.range(5, 5); // readonly []
630
+ * const reverseEmpty = Arr.range(5, 1); // readonly [] (invalid with positive step)
631
+ *
632
+ * // SmallUint constraint examples (0-255 for precise typing)
633
+ * const small = Arr.range(0, 10); // readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
634
+ * const maxSmall = Arr.range(250, 255); // readonly [250, 251, 252, 253, 254]
635
+ * const beyondSmall = Arr.range(0, 300); // readonly SafeUint[] (loses precision)
636
+ *
637
+ * // Custom step increments
638
+ * const evens = Arr.range(0, 10, 2); // readonly SafeUint[] -> [0, 2, 4, 6, 8]
639
+ * const odds = Arr.range(1, 10, 2); // readonly SafeUint[] -> [1, 3, 5, 7, 9]
640
+ * const countdown = Arr.range(5, 0, -1); // readonly SafeInt[] -> [5, 4, 3, 2, 1]
641
+ * const bigStep = Arr.range(0, 20, 5); // readonly SafeUint[] -> [0, 5, 10, 15]
642
+ *
643
+ * // Edge cases that return empty arrays
644
+ * const singleElement = Arr.range(3, 4); // readonly [3]
645
+ * const invalidRange = Arr.range(10, 5, 2); // readonly [] (start > end with positive step)
646
+ * const invalidReverse = Arr.range(1, 10, -1); // readonly [] (start < end with negative step)
647
+ * const zeroRange = Arr.range(42, 42); // readonly [] (start equals end)
648
+ *
649
+ * // Runtime ranges lose precise typing but maintain safety
650
+ * const dynamicStart = Math.floor(Math.random() * 10) as SafeInt;
651
+ * const dynamicEnd = (dynamicStart + 5) as SafeInt;
652
+ * const dynamicRange = Arr.range(dynamicStart, dynamicEnd); // readonly SafeInt[]
653
+ *
654
+ * // Negative numbers and mixed signs
655
+ * const negativeRange = Arr.range(-5, 5); // readonly SafeInt[] -> [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
656
+ * const negativeCountdown = Arr.range(0, -5, -1); // readonly SafeInt[] -> [0, -1, -2, -3, -4]
657
+ *
658
+ * // Useful for generating index ranges and iteration
659
+ * const indices = Arr.range(0, 10); // readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
660
+ * const reversedIndices = Arr.range(9, -1, -1); // readonly SafeInt[] -> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
661
+ *
662
+ * // Functional programming patterns
663
+ * const squares = Arr.range(1, 6).map(x => x * x); // [1, 4, 9, 16, 25]
664
+ * const fibonacci = Arr.range(0, 10).reduce((acc, _, i) => {\n * if (i <= 1) return [...acc, i];\n * return [...acc, acc[i-1] + acc[i-2]];\n * }, [] as number[]); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
665
+ *
666
+ * // Type inference examples showing precise vs general types
667
+ * expectType<typeof range1to4, readonly [1, 2, 3, 4]>('='); // Precise tuple
668
+ * expectType<typeof emptyRange, readonly []>('='); // Precise empty tuple
669
+ * expectType<typeof evens, readonly SafeUint[]>('='); // General positive array
670
+ * expectType<typeof countdown, readonly SafeInt[]>('='); // General integer array
671
+ * expectType<typeof negativeRange, readonly SafeInt[]>('='); // General integer array
672
+ * expectType<typeof small, readonly [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]>('='); // Precise tuple
673
+ * expectType<typeof beyondSmall, readonly SafeUint[]>('='); // General array (beyond SmallUint)
674
+ * ```
675
+ *
676
+ * @throws Never throws - invalid ranges simply return empty arrays
677
+ * @see {@link seq} for creating sequences starting from 0
678
+ * @see {@link SmallUint} for understanding the constraint that enables precise typing
679
+ * @see {@link SafeInt} and {@link SafeUint} for the safe integer types used
680
+ */
681
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
682
+ Arr.range = ((start, end, step = 1) => Array.from(range(start, end, step)));
683
+ // element access
684
+ /**
685
+ * Safely retrieves an element at a given index from an array, returning an {@link Optional}.
686
+ *
687
+ * This function provides type-safe array access with support for negative indexing
688
+ * (e.g., -1 for the last element). Unlike direct array access which can return
689
+ * `undefined` for out-of-bounds indices, this function always returns a well-typed
690
+ * {@link Optional} that explicitly represents the possibility of absence.
691
+ *
692
+ * **Negative Indexing:** Negative indices count from the end of the array:
693
+ * - `-1` refers to the last element
694
+ * - `-2` refers to the second-to-last element, etc.
695
+ *
696
+ * **Curried Usage:** This function supports currying - when called with only an index, it returns
697
+ * a function that can be applied to arrays, making it ideal for use in pipe operations.
698
+ *
699
+ * **Optional Return Type:** The return type is always {@link Optional}<E> which provides:
700
+ * - Type-safe access without `undefined` in your business logic
701
+ * - Explicit handling of \"not found\" cases
702
+ * - Composable error handling with {@link Optional} utilities
703
+ *
704
+ * @template E The type of elements in the array.
705
+ * @param array The array to access (when using direct call syntax).
706
+ * @param index The index to access. Must be a branded `SizeType.ArgArr` (safe integer). Can be:
707
+ * - **Positive integer:** 0-based index from the start (0, 1, 2, ...)
708
+ * - **Negative integer:** index from the end (-1 is last element, -2 is second-to-last, etc.)
709
+ * - **Out of bounds:** any index beyond array bounds returns {@link Optional.None}
710
+ * @returns An {@link Optional}<E> containing:
711
+ * - {@link Optional.Some}<E> with the element if the index is valid
712
+ * - {@link Optional.None} if the index is out of bounds (including empty arrays)
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * // Direct usage with positive indices
717
+ * const fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
718
+ * const first = Arr.at(fruits, 0); // Optional.Some('apple')
719
+ * const third = Arr.at(fruits, 2); // Optional.Some('cherry')
720
+ * const outOfBounds = Arr.at(fruits, 10); // Optional.None
721
+ *
722
+ * // Negative indexing (accessing from the end)
723
+ * const last = Arr.at(fruits, -1); // Optional.Some('elderberry')
724
+ * const secondLast = Arr.at(fruits, -2); // Optional.Some('date')
725
+ * const negativeOutOfBounds = Arr.at(fruits, -10); // Optional.None
726
+ *
727
+ * // Edge cases
728
+ * const emptyResult = Arr.at([], 0); // Optional.None
729
+ * const negativeOnEmpty = Arr.at([], -1); // Optional.None
730
+ * const singleElement = Arr.at(['only'], 0); // Optional.Some('only')
731
+ * const singleNegative = Arr.at(['only'], -1); // Optional.Some('only')
732
+ *
733
+ * // Safe access pattern with type-safe unwrapping
734
+ * const maybeElement = Arr.at(fruits, 2);
735
+ * if (Optional.isSome(maybeElement)) {
736
+ * console.log(`Found: ${maybeElement.value}`); // Type-safe access, no undefined
737
+ * } else {
738
+ * console.log('Index out of bounds');
739
+ * }
740
+ *
741
+ * // Alternative unwrapping with default
742
+ * const elementOrDefault = Optional.unwrapOr(Arr.at(fruits, 100), 'not found');
743
+ * console.log(elementOrDefault); // 'not found'
744
+ *
745
+ * // Curried usage for functional composition
746
+ * const getSecondElement = Arr.at(1);
747
+ * const getLastElement = Arr.at(-1);
748
+ * const getMiddleElement = Arr.at(2);
749
+ *
750
+ * const nestedArrays = [
751
+ * [10, 20, 30, 40],
752
+ * [50, 60],
753
+ * [70]
754
+ * ];
755
+ * const secondElements = nestedArrays.map(getSecondElement);
756
+ * // [Optional.Some(20), Optional.None, Optional.None]
757
+ *
758
+ * const lastElements = nestedArrays.map(getLastElement);
759
+ * // [Optional.Some(40), Optional.Some(60), Optional.Some(70)]
760
+ *
761
+ * // Pipe composition for data processing
762
+ * const processArray = (arr: readonly string[]) => pipe(arr)
763
+ * .map(getSecondElement)
764
+ * .map(opt => Optional.map(opt, s => s.toUpperCase()))
765
+ * .map(opt => Optional.unwrapOr(opt, 'MISSING'))
766
+ * .value;
767
+ *
768
+ * console.log(processArray(['a', 'b', 'c'])); // 'B'
769
+ * console.log(processArray(['x'])); // 'MISSING'
770
+ *
771
+ * // Advanced curried usage with transformation pipelines
772
+ * const extractAndProcess = pipe([
773
+ * ['hello', 'world', 'typescript'],
774
+ * ['functional', 'programming'],
775
+ * ['type', 'safety', 'matters', 'most']
776
+ * ])
777
+ * .map(arr => arr.map(getSecondElement))
778
+ * .map(opts => opts.map(opt => Optional.unwrapOr(opt, '[missing]')))
779
+ * .value;
780
+ * // [['world'], ['[missing]'], ['safety']]
781
+ *
782
+ * // Type inference examples
783
+ * expectType<typeof first, Optional<string>>('=');
784
+ * expectType<typeof getSecondElement, <T>(array: readonly T[]) => Optional<T>>('=');
785
+ * expectType<typeof negativeOutOfBounds, Optional<string>>('=');
786
+ * ```
787
+ *
788
+ * @see {@link head} for getting the first element specifically
789
+ * @see {@link last} for getting the last element specifically
790
+ * @see {@link Optional} for working with the returned Optional values
791
+ * @see {@link Optional.unwrapOr} for safe unwrapping with defaults
792
+ * @see {@link Optional.map} for transforming Optional values
793
+ */
794
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
795
+ Arr.at = ((...args) => {
796
+ switch (args.length) {
797
+ case 2: {
798
+ const [array, index] = args;
799
+ return pipe(index < 0 ? array.length + index : index).map((normalizedIndex) => normalizedIndex < 0 || normalizedIndex >= array.length
800
+ ? Optional.none
801
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
802
+ Optional.some(array[normalizedIndex])).value;
803
+ }
804
+ case 1: {
805
+ const [index] = args;
806
+ return (array) => Arr.at(array, index);
807
+ }
808
+ }
809
+ });
810
+ /**
811
+ * Returns the first element of an array wrapped in an Optional.
812
+ *
813
+ * This function provides type-safe access to the first element with precise return types:
814
+ * - For empty arrays: returns `Optional.None`
815
+ * - For tuples with known first element: returns `Optional.Some<FirstElementType>`
816
+ * - For non-empty arrays: returns `Optional.Some<ElementType>`
817
+ * - For general arrays: returns `Optional<ElementType>`
818
+ *
819
+ * The function leverages TypeScript's type system to provide the most precise return type
820
+ * based on the input array type, making it safer than direct indexing.
821
+ *
822
+ * @template E The type of elements in the array.
823
+ * @param array The array to get the first element from.
824
+ * @returns An Optional containing the first element:
825
+ * - `Optional.None` if the array is empty
826
+ * - `Optional.Some<E>` containing the first element if the array is non-empty
827
+ *
828
+ * @example
829
+ * ```typescript
830
+ * // Empty array - precise None type
831
+ * const emptyResult = Arr.head([]); // Optional.None
832
+ * console.log(Optional.isNone(emptyResult)); // true
833
+ *
834
+ * // Tuple with known structure - precise Some type
835
+ * const tupleResult = Arr.head(['first', 'second', 'third'] as const);
836
+ * // Type: Optional.Some<'first'>
837
+ * if (Optional.isSome(tupleResult)) {
838
+ * console.log(tupleResult.value); // 'first' - TypeScript knows exact type
839
+ * }
840
+ *
841
+ * // Non-empty array - guaranteed Some type
842
+ * const nonEmpty: NonEmptyArray<number> = [10, 20, 30] as NonEmptyArray<number>;
843
+ * const guaranteedResult = Arr.head(nonEmpty); // Optional.Some<number>
844
+ * // No need to check - always Some for NonEmptyArray
845
+ *
846
+ * // General array - may be Some or None
847
+ * const generalArray: number[] = [1, 2, 3];
848
+ * const maybeResult = Arr.head(generalArray); // Optional<number>
849
+ * if (Optional.isSome(maybeResult)) {
850
+ * console.log(`First element: ${maybeResult.value}`);
851
+ * } else {
852
+ * console.log('Array is empty');
853
+ * }
854
+ *
855
+ * // Working with different types
856
+ * const strings = ['hello', 'world'];
857
+ * const firstString = Arr.head(strings); // Optional<string>
858
+ *
859
+ * const objects = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
860
+ * const firstObject = Arr.head(objects); // Optional<{id: number, name: string}>
861
+ *
862
+ * // Functional composition
863
+ * const getFirstElements = (arrays: readonly number[][]) =>
864
+ * arrays.map(Arr.head).filter(Optional.isSome);
865
+ *
866
+ * const nestedArrays = [[1, 2], [3, 4], [], [5]];
867
+ * const firstElements = getFirstElements(nestedArrays);
868
+ * // [Optional.Some(1), Optional.Some(3), Optional.Some(5)]
869
+ *
870
+ * // Type inference examples
871
+ * expectType<typeof emptyResult, Optional.None>('=');
872
+ * expectType<typeof tupleResult, Optional.Some<'first'>>('=');
873
+ * expectType<typeof guaranteedResult, Optional.Some<number>>('=');
874
+ * expectType<typeof maybeResult, Optional<number>>('=');
875
+ * ```
876
+ *
877
+ * @see {@link last} for getting the last element
878
+ * @see {@link at} for accessing elements at specific indices
879
+ * @see {@link tail} for getting all elements except the first
880
+ */
881
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
882
+ Arr.head = ((array) => {
883
+ const element = array.at(0);
884
+ return element === undefined ? Optional.none : Optional.some(element);
885
+ });
886
+ /**
887
+ * Returns the last element of an array wrapped in an Optional.
888
+ *
889
+ * This function provides type-safe access to the last element with precise return types:
890
+ * - For empty arrays: returns `Optional.None`
891
+ * - For tuples with known last element: returns `Optional.Some<LastElementType>`
892
+ * - For non-empty arrays: returns `Optional.Some<ElementType>`
893
+ * - For general arrays: returns `Optional<ElementType>`
894
+ *
895
+ * The function leverages TypeScript's type system to provide the most precise return type
896
+ * based on the input array type, making it safer than direct indexing.
897
+ *
898
+ * @template E The type of elements in the array.
899
+ * @param array The array to get the last element from.
900
+ * @returns An Optional containing the last element:
901
+ * - `Optional.None` if the array is empty
902
+ * - `Optional.Some<E>` containing the last element if the array is non-empty
903
+ *
904
+ * @example
905
+ * ```typescript
906
+ * // Empty array - precise None type
907
+ * const emptyResult = Arr.last([]); // Optional.None
908
+ * console.log(Optional.isNone(emptyResult)); // true
909
+ *
910
+ * // Tuple with known structure - precise Some type
911
+ * const tupleResult = Arr.last(['first', 'middle', 'last'] as const);
912
+ * // Type: Optional.Some<'last'>
913
+ * if (Optional.isSome(tupleResult)) {
914
+ * console.log(tupleResult.value); // 'last' - TypeScript knows exact type
915
+ * }
916
+ *
917
+ * // Non-empty array - guaranteed Some type
918
+ * const nonEmpty: NonEmptyArray<number> = [10, 20, 30] as NonEmptyArray<number>;
919
+ * const guaranteedResult = Arr.last(nonEmpty); // Optional.Some<number>
920
+ * // No need to check - always Some for NonEmptyArray
921
+ *
922
+ * // General array - may be Some or None
923
+ * const generalArray: number[] = [1, 2, 3];
924
+ * const maybeResult = Arr.last(generalArray); // Optional<number>
925
+ * if (Optional.isSome(maybeResult)) {
926
+ * console.log(`Last element: ${maybeResult.value}`);
927
+ * } else {
928
+ * console.log('Array is empty');
929
+ * }
930
+ *
931
+ * // Working with different types
932
+ * const strings = ['hello', 'world', 'example'];
933
+ * const lastString = Arr.last(strings); // Optional<string>
934
+ *
935
+ * const coordinates = [{x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2}];
936
+ * const lastCoordinate = Arr.last(coordinates); // Optional<{x: number, y: number}>
937
+ *
938
+ * // Single element arrays
939
+ * const single = [42];
940
+ * const singleResult = Arr.last(single); // Optional<number> containing 42
941
+ *
942
+ * // Functional composition with arrays of arrays
943
+ * const getLastElements = (arrays: readonly string[][]) =>
944
+ * arrays.map(Arr.last).filter(Optional.isSome);
945
+ *
946
+ * const nestedArrays = [['a', 'b'], ['c'], [], ['d', 'e', 'f']];
947
+ * const lastElements = getLastElements(nestedArrays);
948
+ * // [Optional.Some('b'), Optional.Some('c'), Optional.Some('f')]
949
+ *
950
+ * // Common pattern: get last element or default
951
+ * const data = [10, 20, 30];
952
+ * const lastOrDefault = Optional.unwrapOr(Arr.last(data), 0); // 30
953
+ * const emptyLastOrDefault = Optional.unwrapOr(Arr.last([]), 0); // 0
954
+ *
955
+ * // Type inference examples
956
+ * expectType<typeof emptyResult, Optional.None>('=');
957
+ * expectType<typeof tupleResult, Optional.Some<'last'>>('=');
958
+ * expectType<typeof guaranteedResult, Optional.Some<number>>('=');
959
+ * expectType<typeof maybeResult, Optional<number>>('=');
960
+ * ```
961
+ *
962
+ * @see {@link head} for getting the first element
963
+ * @see {@link at} for accessing elements at specific indices with negative indexing support
964
+ * @see {@link butLast} for getting all elements except the last
965
+ */
966
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
967
+ Arr.last = ((array) => {
968
+ const element = array.at(-1);
969
+ return element === undefined ? Optional.none : Optional.some(element);
970
+ });
971
+ // slicing
972
+ /**
973
+ * Slices an array with automatically clamped start and end indices for safe bounds handling.
974
+ *
975
+ * This function provides a safer alternative to `Array.slice()` by automatically clamping
976
+ * the start and end indices to valid bounds, preventing out-of-bounds access and ensuring
977
+ * consistent behavior regardless of input values.
978
+ *
979
+ * **Clamping Behavior:**
980
+ * - `start` is clamped to `[0, array.length]`
981
+ * - `end` is clamped to `[clampedStart, array.length]` (ensuring end ≥ start)
982
+ * - Invalid ranges (start > end after clamping) return empty arrays
983
+ * - Negative indices are clamped to 0, large indices are clamped to array.length
984
+ *
985
+ * **Curried Usage:** This function supports currying - when called with only start and end
986
+ * indices, it returns a function that can be applied to arrays.
987
+ *
988
+ * @template E The type of elements in the array.
989
+ * @param array The array to slice (when using direct call syntax).
990
+ * @param start The start index for the slice (inclusive). Will be clamped to valid bounds.
991
+ * @param end The end index for the slice (exclusive). Will be clamped to valid bounds.
992
+ * @returns A new immutable array containing the sliced elements. Always returns a valid array,
993
+ * never throws for out-of-bounds indices.
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * const data = [10, 20, 30, 40, 50];
998
+ *
999
+ * // Normal slicing
1000
+ * const middle = Arr.sliceClamped(data, 1, 4); // [20, 30, 40]
1001
+ * const beginning = Arr.sliceClamped(data, 0, 2); // [10, 20]
1002
+ * const end = Arr.sliceClamped(data, 3, 5); // [40, 50]
1003
+ *
1004
+ * // Automatic clamping for out-of-bounds indices
1005
+ * const clampedStart = Arr.sliceClamped(data, -10, 3); // [10, 20, 30] (start clamped to 0)
1006
+ * const clampedEnd = Arr.sliceClamped(data, 2, 100); // [30, 40, 50] (end clamped to length)
1007
+ * const bothClamped = Arr.sliceClamped(data, -5, 100); // [10, 20, 30, 40, 50] (entire array)
1008
+ *
1009
+ * // Invalid ranges become empty arrays
1010
+ * const emptyReversed = Arr.sliceClamped(data, 4, 1); // [] (start > end after clamping)
1011
+ * const emptyAtEnd = Arr.sliceClamped(data, 5, 10); // [] (start at end of array)
1012
+ *
1013
+ * // Edge cases
1014
+ * const emptyArray = Arr.sliceClamped([], 0, 5); // [] (empty input)
1015
+ * const singleElement = Arr.sliceClamped([42], 0, 1); // [42]
1016
+ * const fullCopy = Arr.sliceClamped(data, 0, data.length); // [10, 20, 30, 40, 50]
1017
+ *
1018
+ * // Curried usage for functional composition
1019
+ * const takeFirst3 = Arr.sliceClamped(0, 3);
1020
+ * const getMiddle2 = Arr.sliceClamped(1, 3);
1021
+ *
1022
+ * const arrays = [
1023
+ * [1, 2, 3, 4, 5],
1024
+ * [10, 20],
1025
+ * [100, 200, 300, 400, 500, 600]
1026
+ * ];
1027
+ *
1028
+ * const first3Elements = arrays.map(takeFirst3);
1029
+ * // [[1, 2, 3], [10, 20], [100, 200, 300]]
1030
+ *
1031
+ * const middle2Elements = arrays.map(getMiddle2);
1032
+ * // [[2, 3], [20], [200, 300]]
1033
+ *
1034
+ * // Pipe composition
1035
+ * const result = pipe([1, 2, 3, 4, 5, 6])
1036
+ * .map(takeFirst3)
1037
+ * .map(Arr.sum)
1038
+ * .value; // 6 (sum of [1, 2, 3])
1039
+ *
1040
+ * // Comparison with regular Array.slice (which can throw or behave unexpectedly)
1041
+ * try {
1042
+ * // Regular slice with out-of-bounds - works but may be unintuitive
1043
+ * const regularSlice = data.slice(-10, 100); // [10, 20, 30, 40, 50]
1044
+ * // sliceClamped provides same safe behavior explicitly
1045
+ * const clampedSlice = Arr.sliceClamped(data, -10, 100); // [10, 20, 30, 40, 50]
1046
+ * } catch (error) {
1047
+ * // sliceClamped never throws
1048
+ * }
1049
+ * ```
1050
+ *
1051
+ * @see {@link take} for taking the first N elements
1052
+ * @see {@link skip} for skipping the first N elements
1053
+ * @see {@link takeLast} for taking the last N elements
1054
+ */
1055
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1056
+ Arr.sliceClamped = ((...args) => {
1057
+ switch (args.length) {
1058
+ case 3: {
1059
+ const [array, start, end] = args;
1060
+ const startClamped = Num.clamp(0, array.length)(start);
1061
+ // Ensure endClamped is not less than startClamped.
1062
+ const endClamped = Num.clamp(startClamped, array.length)(end);
1063
+ return array.slice(startClamped, endClamped);
1064
+ }
1065
+ case 2: {
1066
+ const [start, end] = args;
1067
+ return (array) => Arr.sliceClamped(array, start, end);
1068
+ }
1069
+ }
1070
+ });
1071
+ /**
1072
+ * Returns all elements of an array except the first one.
1073
+ * @template E The type of the array (can be a tuple for more precise typing).
1074
+ * @param array The input array.
1075
+ * @returns A new array containing all elements except the first. The type is inferred as `List.Tail<T>`.
1076
+ * @example
1077
+ * ```ts
1078
+ * Arr.tail([1, 2, 3] as const); // [2, 3]
1079
+ * Arr.tail([1] as const); // []
1080
+ * Arr.tail([]); // []
1081
+ * ```
1082
+ */
1083
+ Arr.tail = (array) =>
1084
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1085
+ array.slice(1);
1086
+ /**
1087
+ * Returns all elements of an array except the last one.
1088
+ * @template E The type of the array (can be a tuple for more precise typing).
1089
+ * @param array The input array.
1090
+ * @returns A new array containing all elements except the last. The type is inferred as `List.ButLast<T>`.
1091
+ * @example
1092
+ * ```ts
1093
+ * Arr.butLast([1, 2, 3] as const); // [1, 2]
1094
+ * Arr.butLast([1] as const); // []
1095
+ * Arr.butLast([]); // []
1096
+ * ```
1097
+ */
1098
+ Arr.butLast = (array) =>
1099
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1100
+ (Arr.isEmpty(array) ? [] : array.slice(0, -1));
1101
+ /**
1102
+ * Takes the first N elements from an array.
1103
+ *
1104
+ * - If the array is a tuple, the return type is inferred as a tuple of the first N elements.
1105
+ * - If the array is a NonEmptyArray and N is a SizeType.ArgArrPositive, returns a NonEmptyArray.
1106
+ * - Otherwise, returns a readonly array of up to N elements.
1107
+ *
1108
+ * @template E The type of the array (can be a tuple for more precise typing).
1109
+ * @template N The number of elements to take, constrained to `SmallUint`.
1110
+ * @param array The input array.
1111
+ * @param num The number of elements to take.
1112
+ * @returns A new array containing the first N elements.
1113
+ * @example
1114
+ * ```ts
1115
+ * // Regular usage
1116
+ * Arr.take([1, 2, 3, 4] as const, 2); // [1, 2]
1117
+ *
1118
+ * // Curried usage for pipe composition
1119
+ * const takeFirst3 = Arr.take(3);
1120
+ * const result = pipe([1, 2, 3, 4, 5]).map(takeFirst3).value;
1121
+ * console.log(result); // [1, 2, 3]
1122
+ * ```
1123
+ */
1124
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1125
+ Arr.take = ((...args) => {
1126
+ switch (args.length) {
1127
+ case 2: {
1128
+ const [array, num] = args;
1129
+ return Arr.sliceClamped(array, 0, num);
1130
+ }
1131
+ case 1: {
1132
+ const [num] = args;
1133
+ return (array) => Arr.take(array, num);
1134
+ }
1135
+ }
1136
+ });
1137
+ /**
1138
+ * Takes the last N elements from an array.
1139
+ *
1140
+ * - If the array is a tuple, the return type is inferred as a tuple of the last N elements.
1141
+ * - If the array is a non-empty array and N is a positive integer, returns a non-empty array.
1142
+ * - Otherwise, returns a readonly array of up to N elements.
1143
+ *
1144
+ * @template E The type of the array (can be a tuple for more precise typing).
1145
+ * @template N The number of elements to take, constrained to `SmallUint`.
1146
+ * @param array The input array.
1147
+ * @param num The number of elements to take.
1148
+ * @returns A new array containing the last N elements.
1149
+ * @example
1150
+ * ```ts
1151
+ * // Regular usage
1152
+ * Arr.takeLast([1, 2, 3, 4] as const, 2); // [3, 4]
1153
+ *
1154
+ * // Curried usage for pipe composition
1155
+ * const takeLast2 = Arr.takeLast(2);
1156
+ * const result = pipe([1, 2, 3, 4, 5]).map(takeLast2).value;
1157
+ * console.log(result); // [4, 5]
1158
+ * ```
1159
+ */
1160
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1161
+ Arr.takeLast = ((...args) => {
1162
+ switch (args.length) {
1163
+ case 2: {
1164
+ const [array, num] = args;
1165
+ return Arr.sliceClamped(array, Uint32.sub(Arr.size(array), num), Arr.size(array));
1166
+ }
1167
+ case 1: {
1168
+ const [num] = args;
1169
+ return (array) => Arr.takeLast(array, num);
1170
+ }
1171
+ }
1172
+ });
1173
+ /**
1174
+ * Skips the first N elements of an array.
1175
+ *
1176
+ * - If the array is a tuple, the return type is inferred as a tuple with the first N elements removed.
1177
+ * - If the array is a non-empty array and N is a positive integer, returns a readonly array (may be empty).
1178
+ * - Otherwise, returns a readonly array with the first N elements skipped.
1179
+ *
1180
+ * @template E The type of the array (can be a tuple for more precise typing).
1181
+ * @template N The number of elements to skip, constrained to `SmallUint`.
1182
+ * @param array The input array.
1183
+ * @param num The number of elements to skip.
1184
+ * @returns A new array containing the elements after skipping the first N.
1185
+ * @example
1186
+ * ```ts
1187
+ * // Regular usage
1188
+ * Arr.skip([1, 2, 3, 4] as const, 2); // [3, 4]
1189
+ *
1190
+ * // Curried usage for pipe composition
1191
+ * const skipFirst2 = Arr.skip(2);
1192
+ * const result = pipe([1, 2, 3, 4, 5]).map(skipFirst2).value;
1193
+ * console.log(result); // [3, 4, 5]
1194
+ * ```
1195
+ */
1196
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1197
+ Arr.skip = ((...args) => {
1198
+ switch (args.length) {
1199
+ case 2: {
1200
+ const [array, num] = args;
1201
+ return Arr.sliceClamped(array, num, Arr.size(array));
1202
+ }
1203
+ case 1: {
1204
+ const [num] = args;
1205
+ return (array) => Arr.skip(array, num);
1206
+ }
1207
+ }
1208
+ });
1209
+ /**
1210
+ * Skips the last N elements of an array.
1211
+ *
1212
+ * - If the array is a tuple, the return type is inferred as a tuple with the last N elements removed.
1213
+ * - If the array is a non-empty array and N is a positive integer, returns a readonly array (may be empty).
1214
+ * - Otherwise, returns a readonly array with the last N elements skipped.
1215
+ *
1216
+ * @template E The type of the array (can be a tuple for more precise typing).
1217
+ * @template N The number of elements to skip, constrained to `SmallUint`.
1218
+ * @param array The input array.
1219
+ * @param num The number of elements to skip from the end.
1220
+ * @returns A new array containing the elements after skipping the last N.
1221
+ * @example
1222
+ * ```ts
1223
+ * // Regular usage
1224
+ * Arr.skipLast([1, 2, 3, 4] as const, 2); // [1, 2]
1225
+ *
1226
+ * // Curried usage for pipe composition
1227
+ * const skipLast2 = Arr.skipLast(2);
1228
+ * const result = pipe([1, 2, 3, 4, 5]).map(skipLast2).value;
1229
+ * console.log(result); // [1, 2, 3]
1230
+ * ```
1231
+ */
1232
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1233
+ Arr.skipLast = ((...args) => {
1234
+ switch (args.length) {
1235
+ case 2: {
1236
+ const [array, num] = args;
1237
+ return Arr.sliceClamped(array, 0, Uint32.sub(Arr.size(array), num));
1238
+ }
1239
+ case 1: {
1240
+ const [num] = args;
1241
+ return (array) => Arr.skipLast(array, num);
1242
+ }
1243
+ }
1244
+ });
1245
+ // modification (returns new array)
1246
+ /**
1247
+ * Returns a new array with the element at the specified index updated by a function.
1248
+ *
1249
+ * This function provides immutable array updates with type-safe bounds checking. It applies an updater
1250
+ * function to the element at the given index and returns a new array with the transformed value.
1251
+ * The original array is never modified, ensuring immutability.
1252
+ *
1253
+ * **Type Union Behavior:** When the updater function returns a different type `U` than the original
1254
+ * element type `E`, the result type becomes `readonly (E | U)[]` to accommodate both original and
1255
+ * updated element types. This ensures type safety when elements have different types after updating.
1256
+ *
1257
+ * **Bounds Checking:** Unlike native array access which can cause runtime errors, this function
1258
+ * performs safe bounds checking:
1259
+ * - **Valid index:** Creates new array with updated element
1260
+ * - **Invalid index:** Returns the original array unchanged (no errors thrown)
1261
+ * - **Negative index:** Treated as invalid (returns original array)
1262
+ *
1263
+ * **Curried Usage:** Supports currying for functional composition - when called with only index and
1264
+ * updater, returns a reusable function that can be applied to arrays.
1265
+ *
1266
+ * @template E The type of elements in the original array.
1267
+ * @template U The type of the value returned by the updater function.
1268
+ * @param array The input array to update. Can be any readonly array.
1269
+ * @param index The index of the element to update. Must be a non-negative {@link SizeType.ArgArrNonNegative}.
1270
+ * - **Valid range:** `0 <= index < array.length`
1271
+ * - **Out of bounds:** Returns original array unchanged
1272
+ * - **Negative values:** Not allowed by type system (non-negative constraint)
1273
+ * @param updater A function `(prev: E) => U` that transforms the existing element:
1274
+ * - **prev:** The current element at the specified index
1275
+ * - **returns:** The new value to place at that index (can be different type)
1276
+ * @returns A new `readonly (E | U)[]` array where:
1277
+ * - All elements except the target index remain unchanged (type `E`)
1278
+ * - The element at the target index is replaced with the updater result (type `U`)
1279
+ * - Type union `E | U` accommodates both original and updated element types
1280
+ * - If index is out of bounds, returns the original array unchanged
1281
+ *
1282
+ * @example
1283
+ * ```typescript
1284
+ * // Basic usage with same type transformation
1285
+ * const numbers = [1, 2, 3, 4, 5];
1286
+ * const doubled = Arr.toUpdated(numbers, 2, x => x * 2);
1287
+ * // readonly number[] -> [1, 2, 6, 4, 5]
1288
+ *
1289
+ * // Type union when updater returns different type
1290
+ * const mixed = Arr.toUpdated(numbers, 1, x => `value: ${x}`);
1291
+ * // readonly (number | string)[] -> [1, 'value: 2', 3, 4, 5]
1292
+ *
1293
+ * // Complex object updates
1294
+ * const users = [
1295
+ * { id: 1, name: 'Alice', active: true },
1296
+ * { id: 2, name: 'Bob', active: false },
1297
+ * { id: 3, name: 'Charlie', active: true }
1298
+ * ];
1299
+ *
1300
+ * const activatedUser = Arr.toUpdated(users, 1, user => ({
1301
+ * ...user,
1302
+ * active: true,
1303
+ * lastUpdated: new Date()
1304
+ * }));
1305
+ * // Bob is now active with lastUpdated field
1306
+ *
1307
+ * // Bounds checking behavior
1308
+ * const safe1 = Arr.toUpdated([1, 2, 3], 10, x => x * 2); // [1, 2, 3] (index out of bounds)
1309
+ * const safe2 = Arr.toUpdated([1, 2, 3], 0, x => x * 2); // [2, 2, 3] (valid index)
1310
+ * const safe3 = Arr.toUpdated([], 0, x => x); // [] (empty array, index out of bounds)
1311
+ *
1312
+ * // Functional transformations
1313
+ * const products = [
1314
+ * { name: 'laptop', price: 1000 },
1315
+ * { name: 'mouse', price: 25 },
1316
+ * { name: 'keyboard', price: 75 }
1317
+ * ];
1318
+ *
1319
+ * const discounted = Arr.toUpdated(products, 0, product => ({
1320
+ * ...product,
1321
+ * price: Math.round(product.price * 0.8), // 20% discount
1322
+ * onSale: true
1323
+ * }));
1324
+ * // First product now has discounted price and onSale flag
1325
+ *
1326
+ * // Curried usage for reusable updates
1327
+ * const doubleAtIndex2 = Arr.toUpdated(2, (x: number) => x * 2);
1328
+ * const capitalizeAtIndex0 = Arr.toUpdated(0, (s: string) => s.toUpperCase());
1329
+ * const markCompleteAtIndex = (index: number) =>
1330
+ * Arr.toUpdated(index, (task: {done: boolean}) => ({...task, done: true}));
1331
+ *
1332
+ * const numberArrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1333
+ * const allDoubled = numberArrays.map(doubleAtIndex2);
1334
+ * // [[1, 2, 6], [4, 5, 12], [7, 8, 18]]
1335
+ *
1336
+ * const words = [['hello', 'world'], ['foo', 'bar'], ['type', 'script']];
1337
+ * const capitalized = words.map(capitalizeAtIndex0);
1338
+ * // [['HELLO', 'world'], ['FOO', 'bar'], ['TYPE', 'script']]
1339
+ *
1340
+ * // Pipe composition for data processing
1341
+ * const processArray = (arr: readonly number[]) => pipe(arr)
1342
+ * .map(Arr.toUpdated(0, x => x * 10)) // Scale first element
1343
+ * .map(Arr.toUpdated(1, x => typeof x === 'number' ? x + 100 : x)) // Add to second if number
1344
+ * .value;
1345
+ *
1346
+ * console.log(processArray([1, 2, 3])); // [10, 102, 3]
1347
+ *
1348
+ * // Multiple sequential updates
1349
+ * const pipeline = (data: readonly number[]) => pipe(data)
1350
+ * .map(Arr.toUpdated(0, x => x * 2))
1351
+ * .map(Arr.toUpdated(1, x => typeof x === 'number' ? x + 10 : x))
1352
+ * .map(Arr.toUpdated(2, x => typeof x === 'number' ? x.toString() : x))
1353
+ * .value;
1354
+ *
1355
+ * console.log(pipeline([1, 2, 3])); // [2, 12, '3'] - readonly (number | string)[]
1356
+ *
1357
+ * // Error-safe updates in data processing
1358
+ * const safeUpdate = <T, U>(
1359
+ * array: readonly T[],
1360
+ * index: number,
1361
+ * updater: (value: T) => U
1362
+ * ) => {
1363
+ * if (index < 0 || index >= array.length) {
1364
+ * console.warn(`Index ${index} out of bounds for array of length ${array.length}`);
1365
+ * return array;
1366
+ * }
1367
+ * return Arr.toUpdated(array, index as SizeType.ArgArrNonNegative, updater);
1368
+ * };
1369
+ *
1370
+ * // Advanced: State management pattern
1371
+ * type AppState = {
1372
+ * users: Array<{id: number, name: string}>;
1373
+ * currentUserId: number;
1374
+ * };
1375
+ *
1376
+ * const updateUserName = (state: AppState, userId: number, newName: string): AppState => {
1377
+ * const userIndex = state.users.findIndex(u => u.id === userId);
1378
+ * if (userIndex === -1) return state;
1379
+ *
1380
+ * return {
1381
+ * ...state,
1382
+ * users: Arr.toUpdated(state.users, userIndex as SizeType.ArgArrNonNegative, user => ({
1383
+ * ...user,
1384
+ * name: newName
1385
+ * }))
1386
+ * };
1387
+ * };
1388
+ *
1389
+ * // Type inference examples showing union types
1390
+ * expectType<typeof doubled, readonly number[]>('='); // Same type
1391
+ * expectType<typeof mixed, readonly (number | string)[]>('='); // Union type
1392
+ * expectType<typeof doubleAtIndex2, <T extends readonly number[]>(array: T) => readonly (number | number)[]>('=');
1393
+ * expectType<typeof safe1, readonly number[]>('='); // Bounds check preserves type
1394
+ * ```
1395
+ *
1396
+ * @see {@link Array.prototype.with} for the native method with different error handling
1397
+ * @see {@link SizeType.ArgArrNonNegative} for the index type constraint
1398
+ * @see Immutable update patterns for functional programming approaches
1399
+ */
1400
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1401
+ Arr.toUpdated = ((...args) => {
1402
+ switch (args.length) {
1403
+ case 3: {
1404
+ const [array, index, updater] = args;
1405
+ return index < 0 || index >= array.length
1406
+ ? array // Return a copy if index is out of bounds
1407
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unsafe-type-assertion
1408
+ array.with(index, updater(array[index]));
1409
+ }
1410
+ case 2: {
1411
+ const [index, updater] = args;
1412
+ return (array) => Arr.toUpdated(array, index, updater);
1413
+ }
1414
+ }
1415
+ });
1416
+ /**
1417
+ * Returns a new array with a new value inserted at the specified index.
1418
+ * Index can be out of bounds (e.g., negative or greater than length), `toSpliced` handles this.
1419
+ * @template E The type of elements in the array.
1420
+ * @param array The input array.
1421
+ * @param index The index at which to insert the new value.
1422
+ * @param newValue The value to insert.
1423
+ * @returns A new array with the value inserted.
1424
+ * @example
1425
+ * ```ts
1426
+ * // Regular usage
1427
+ * Arr.toInserted([1, 2, 3], 1, 10); // [1, 10, 2, 3]
1428
+ *
1429
+ * // Curried usage for pipe composition
1430
+ * const insertAtStart = Arr.toInserted(0, 99);
1431
+ * const result = pipe([1, 2, 3]).map(insertAtStart).value;
1432
+ * console.log(result); // [99, 1, 2, 3]
1433
+ * ```
1434
+ */
1435
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1436
+ Arr.toInserted = ((...args) => {
1437
+ switch (args.length) {
1438
+ case 3: {
1439
+ const [array, index, newValue] = args;
1440
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1441
+ return array.toSpliced(index, 0, newValue);
1442
+ }
1443
+ case 2: {
1444
+ const [index, newValue] = args;
1445
+ return (array) => Arr.toInserted(array, index, newValue);
1446
+ }
1447
+ }
1448
+ });
1449
+ /**
1450
+ * Returns a new array with the element at the specified index removed.
1451
+ * If index is out of bounds, `toSpliced` handles this (usually by returning a copy).
1452
+ * @template E The type of elements in the array.
1453
+ * @param array The input array.
1454
+ * @param index The index of the element to remove.
1455
+ * @returns A new array with the element removed.
1456
+ * @example
1457
+ * ```ts
1458
+ * // Regular usage
1459
+ * Arr.toRemoved([1, 2, 3], 1); // [1, 3]
1460
+ *
1461
+ * // Curried usage for pipe composition
1462
+ * const removeFirst = Arr.toRemoved(0);
1463
+ * const result = pipe([10, 20, 30]).map(removeFirst).value;
1464
+ * console.log(result); // [20, 30]
1465
+ * ```
1466
+ */
1467
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1468
+ Arr.toRemoved = ((...args) => {
1469
+ switch (args.length) {
1470
+ case 2: {
1471
+ const [array, index] = args;
1472
+ return array.toSpliced(index, 1);
1473
+ }
1474
+ case 1: {
1475
+ const [index] = args;
1476
+ return (array) => Arr.toRemoved(array, index);
1477
+ }
1478
+ }
1479
+ });
1480
+ /**
1481
+ * Returns a new array with a value added to the end.
1482
+ * @template E The type of the input array (can be a tuple).
1483
+ * @template V The type of the value to add.
1484
+ * @param array The input array.
1485
+ * @param newValue The value to add.
1486
+ * @returns A new array with the value added to the end. Type is `readonly [...E, V]`.
1487
+ * @example
1488
+ * ```ts
1489
+ * // Regular usage
1490
+ * Arr.toPushed([1, 2] as const, 3); // [1, 2, 3]
1491
+ *
1492
+ * // Curried usage for pipe composition
1493
+ * const addZero = Arr.toPushed(0);
1494
+ * const result = pipe([1, 2, 3]).map(addZero).value;
1495
+ * console.log(result); // [1, 2, 3, 0]
1496
+ * ```
1497
+ */
1498
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1499
+ Arr.toPushed = ((...args) => {
1500
+ switch (args.length) {
1501
+ case 2: {
1502
+ const [array, newValue] = args;
1503
+ return array.toSpliced(array.length, 0, newValue);
1504
+ }
1505
+ case 1: {
1506
+ const [newValue] = args;
1507
+ return (array) => Arr.toPushed(array, newValue);
1508
+ }
1509
+ }
1510
+ });
1511
+ /**
1512
+ * Returns a new array with a value added to the beginning.
1513
+ * @template E The type of the input array (can be a tuple).
1514
+ * @template V The type of the value to add.
1515
+ * @param array The input array.
1516
+ * @param newValue The value to add.
1517
+ * @returns A new array with the value added to the beginning. Type is `readonly [V, ...E]`.
1518
+ * @example
1519
+ * ```ts
1520
+ * // Regular usage
1521
+ * Arr.toUnshifted([1, 2] as const, 0); // [0, 1, 2]
1522
+ *
1523
+ * // Curried usage for pipe composition
1524
+ * const prependZero = Arr.toUnshifted(0);
1525
+ * const result = pipe([1, 2, 3]).map(prependZero).value;
1526
+ * console.log(result); // [0, 1, 2, 3]
1527
+ * ```
1528
+ */
1529
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1530
+ Arr.toUnshifted = ((...args) => {
1531
+ switch (args.length) {
1532
+ case 2: {
1533
+ const [array, newValue] = args;
1534
+ return array.toSpliced(0, 0, newValue);
1535
+ }
1536
+ case 1: {
1537
+ const [newValue] = args;
1538
+ return (array) => Arr.toUnshifted(array, newValue);
1539
+ }
1540
+ }
1541
+ });
1542
+ /**
1543
+ * Fills an array with a value (creates a new filled array).
1544
+ * @param array The array.
1545
+ * @param value The value to fill with.
1546
+ * @param start The start index.
1547
+ * @param end The end index.
1548
+ * @returns A new filled array.
1549
+ * @example
1550
+ * ```typescript
1551
+ * // Regular usage
1552
+ * const arr = [1, 2, 3, 4, 5];
1553
+ * const result = Arr.toFilled(arr, 0, 1, 4);
1554
+ * console.log(result); // [1, 0, 0, 0, 5]
1555
+ *
1556
+ * // Curried usage for pipe composition
1557
+ * const fillWithZeros = Arr.toFilled(0, 1, 3);
1558
+ * const result2 = pipe([1, 2, 3, 4]).map(fillWithZeros).value;
1559
+ * console.log(result2); // [1, 0, 0, 4]
1560
+ * ```
1561
+ */
1562
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1563
+ Arr.toFilled = ((...args) => {
1564
+ switch (args.length) {
1565
+ case 2: {
1566
+ const [array, value] = args;
1567
+ const cp = castMutable(Arr.copy(array));
1568
+ cp.fill(value);
1569
+ return cp;
1570
+ }
1571
+ case 1: {
1572
+ const [value] = args;
1573
+ return (array) => Arr.toFilled(array, value);
1574
+ }
1575
+ }
1576
+ });
1577
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1578
+ Arr.toRangeFilled = ((...args) => {
1579
+ switch (args.length) {
1580
+ case 3: {
1581
+ const [array, value, [start, end]] = args;
1582
+ const cp = castMutable(Arr.copy(array));
1583
+ cp.fill(value, start, end);
1584
+ return cp;
1585
+ }
1586
+ case 2: {
1587
+ const [value, fillRange] = args;
1588
+ return (array) => Arr.toRangeFilled(array, value, fillRange);
1589
+ }
1590
+ }
1591
+ });
1592
+ // searching
1593
+ /**
1594
+ * Safely finds the first element in an array that satisfies a predicate function.
1595
+ *
1596
+ * This function provides type-safe searching with no risk of runtime errors. It returns
1597
+ * the first element that matches the predicate wrapped in an Optional, or Optional.None
1598
+ * if no element is found. The predicate receives the element, its index, and the entire array.
1599
+ *
1600
+ * **Curried Usage:** This function supports currying - when called with only a predicate,
1601
+ * it returns a function that can be applied to arrays, making it ideal for functional composition.
1602
+ *
1603
+ * @template E The type of elements in the array.
1604
+ * @param array The array to search through (when using direct call syntax).
1605
+ * @param predicate A function that tests each element. Called with:
1606
+ * - `value`: The current element being tested
1607
+ * - `index`: The index of the current element (branded as `SizeType.Arr`)
1608
+ * - `arr`: The entire array being searched
1609
+ * @returns An `Optional<E>` containing:
1610
+ * - `Optional.Some<E>` with the first matching element if found
1611
+ * - `Optional.None` if no element satisfies the predicate
1612
+ *
1613
+ * @example
1614
+ * ```typescript
1615
+ * // Basic element finding
1616
+ * const numbers = [1, 2, 3, 4, 5];
1617
+ * const firstEven = Arr.find(numbers, x => x % 2 === 0);
1618
+ * if (Optional.isSome(firstEven)) {
1619
+ * console.log(firstEven.value); // 2 - first even number
1620
+ * }
1621
+ *
1622
+ * // Finding with index information
1623
+ * const findLargeAtEnd = Arr.find(numbers, (value, index) => value > 3 && index > 2);
1624
+ * // Optional.Some(4) - first number > 3 after index 2
1625
+ *
1626
+ * // Finding objects by property
1627
+ * const users = [
1628
+ * { id: 1, name: 'Alice', age: 25 },
1629
+ * { id: 2, name: 'Bob', age: 30 },
1630
+ * { id: 3, name: 'Charlie', age: 35 }
1631
+ * ];
1632
+ *
1633
+ * const adult = Arr.find(users, user => user.age >= 30);
1634
+ * // Optional.Some({ id: 2, name: 'Bob', age: 30 })
1635
+ *
1636
+ * const nonExistent = Arr.find(users, user => user.age > 100);
1637
+ * // Optional.None
1638
+ *
1639
+ * // Empty array handling
1640
+ * const emptyResult = Arr.find([], x => x > 0); // Optional.None
1641
+ *
1642
+ * // Curried usage for functional composition
1643
+ * const findNegative = Arr.find((x: number) => x < 0);
1644
+ * const findLongString = Arr.find((s: string) => s.length > 5);
1645
+ *
1646
+ * const datasets = [
1647
+ * [1, 2, -3, 4],
1648
+ * [5, 6, 7, 8],
1649
+ * [-1, 0, 1]
1650
+ * ];
1651
+ *
1652
+ * const negativeNumbers = datasets.map(findNegative);
1653
+ * // [Optional.Some(-3), Optional.None, Optional.Some(-1)]
1654
+ *
1655
+ * // Pipe composition
1656
+ * const result = pipe(['short', 'medium', 'very long string'])
1657
+ * .map(findLongString)
1658
+ * .map(opt => Optional.unwrapOr(opt, 'default'))
1659
+ * .value; // 'very long string'
1660
+ *
1661
+ * // Complex predicate with all parameters
1662
+ * const findSpecial = (arr: readonly number[]) =>
1663
+ * Arr.find(arr, (value, index, array) => {
1664
+ * return value > 10 && index > 0 && index < array.length - 1;
1665
+ * });
1666
+ *
1667
+ * const hasMiddleSpecial = findSpecial([5, 15, 20, 3]);
1668
+ * // Optional.Some(15) - first value > 10 that's not at start or end
1669
+ *
1670
+ * // Safe unwrapping patterns
1671
+ * const maybeFound = Arr.find(numbers, x => x > 10);
1672
+ * const foundOrDefault = Optional.unwrapOr(maybeFound, 0); // 0 (not found)
1673
+ * const foundOrThrow = Optional.isSome(maybeFound)
1674
+ * ? maybeFound.value
1675
+ * : (() => { throw new Error('Not found'); })();
1676
+ *
1677
+ * // Type inference examples
1678
+ * expectType<typeof firstEven, Optional<number>>('=');
1679
+ * expectType<typeof adult, Optional<{id: number, name: string, age: number}>>('=');
1680
+ * expectType<typeof findNegative, (array: readonly number[]) => Optional<number>>('=');
1681
+ * ```
1682
+ *
1683
+ * @see {@link findIndex} for finding the index instead of the element
1684
+ * @see {@link indexOf} for finding elements by equality
1685
+ * @see {@link Optional} for working with the returned Optional values
1686
+ */
1687
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1688
+ Arr.find = ((...args) => {
1689
+ switch (args.length) {
1690
+ case 2: {
1691
+ const [array, predicate] = args;
1692
+ const foundIndex = array.findIndex(
1693
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1694
+ predicate);
1695
+ return foundIndex === -1
1696
+ ? Optional.none
1697
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1698
+ Optional.some(array[foundIndex]);
1699
+ }
1700
+ case 1: {
1701
+ const [predicate] = args;
1702
+ return (array) => Arr.find(array, predicate);
1703
+ }
1704
+ }
1705
+ });
1706
+ /**
1707
+ * Safely finds the index of the first element in an array that satisfies a predicate function.
1708
+ *
1709
+ * This function provides type-safe index searching with no risk of runtime errors. It returns
1710
+ * the index of the first element that matches the predicate wrapped in an Optional, or Optional.None
1711
+ * if no element is found. The returned index is branded as `SizeType.Arr` for type safety.
1712
+ *
1713
+ * **Curried Usage:** This function supports currying - when called with only a predicate,
1714
+ * it returns a function that can be applied to arrays, making it ideal for functional composition.
1715
+ *
1716
+ * @template E The type of elements in the array.
1717
+ * @param array The array to search through (when using direct call syntax).
1718
+ * @param predicate A function that tests each element. Called with:
1719
+ * - `value`: The current element being tested
1720
+ * - `index`: The index of the current element (branded as `SizeType.Arr`)
1721
+ * @returns An `Optional<SizeType.Arr>` containing:
1722
+ * - `Optional.Some<SizeType.Arr>` with the index of the first matching element if found
1723
+ * - `Optional.None` if no element satisfies the predicate
1724
+ *
1725
+ * @example
1726
+ * ```typescript
1727
+ * // Basic index finding
1728
+ * const fruits = ['apple', 'banana', 'cherry', 'banana'];
1729
+ * const bananaIndex = Arr.findIndex(fruits, fruit => fruit === 'banana');
1730
+ * if (Optional.isSome(bananaIndex)) {
1731
+ * console.log(bananaIndex.value); // 1 - index of first 'banana'
1732
+ * }
1733
+ *
1734
+ * // Finding with complex conditions
1735
+ * const numbers = [1, 5, 10, 15, 20];
1736
+ * const firstLargeIndex = Arr.findIndex(numbers, (value, index) =>
1737
+ * value > 10 && index > 1
1738
+ * );
1739
+ * // Optional.Some(3) - index of 15 (first value > 10 after index 1)
1740
+ *
1741
+ * // Finding objects by property
1742
+ * const users = [
1743
+ * { id: 1, active: false },
1744
+ * { id: 2, active: true },
1745
+ * { id: 3, active: true }
1746
+ * ];
1747
+ *
1748
+ * const firstActiveIndex = Arr.findIndex(users, user => user.active);
1749
+ * // Optional.Some(1) - index of first active user
1750
+ *
1751
+ * const inactiveAdminIndex = Arr.findIndex(users, user => !user.active && user.id > 5);
1752
+ * // Optional.None - no inactive user with id > 5
1753
+ *
1754
+ * // Empty array handling
1755
+ * const emptyResult = Arr.findIndex([], x => x > 0); // Optional.None
1756
+ *
1757
+ * // Curried usage for functional composition
1758
+ * const findNegativeIndex = Arr.findIndex((x: number) => x < 0);
1759
+ * const findLongStringIndex = Arr.findIndex((s: string) => s.length > 5);
1760
+ *
1761
+ * const datasets = [
1762
+ * [1, 2, -3, 4], // index 2 has negative
1763
+ * [5, 6, 7, 8], // no negative
1764
+ * [-1, 0, 1] // index 0 has negative
1765
+ * ];
1766
+ *
1767
+ * const negativeIndices = datasets.map(findNegativeIndex);
1768
+ * // [Optional.Some(2), Optional.None, Optional.Some(0)]
1769
+ *
1770
+ * // Using found indices for further operations
1771
+ * const data = ['short', 'medium', 'very long string', 'tiny'];
1772
+ * const longStringIndex = Arr.findIndex(data, s => s.length > 8);
1773
+ *
1774
+ * if (Optional.isSome(longStringIndex)) {
1775
+ * const index = longStringIndex.value;
1776
+ * console.log(`Found at position ${index}: ${data[index]}`);
1777
+ * // "Found at position 2: very long string"
1778
+ * }
1779
+ *
1780
+ * // Pipe composition
1781
+ * const result = pipe(['a', 'bb', 'ccc'])
1782
+ * .map(findLongStringIndex)
1783
+ * .map(opt => Optional.unwrapOr(opt, -1))
1784
+ * .value; // 2 (index of 'ccc')
1785
+ *
1786
+ * // Comparing with native findIndex (which returns -1)
1787
+ * const nativeResult = fruits.findIndex(fruit => fruit === 'grape'); // -1
1788
+ * const safeResult = Arr.findIndex(fruits, fruit => fruit === 'grape'); // Optional.None
1789
+ *
1790
+ * // Safe index usage patterns
1791
+ * const maybeIndex = Arr.findIndex(numbers, x => x > 100);
1792
+ * const indexOrDefault = Optional.unwrapOr(maybeIndex, 0); // 0 (not found)
1793
+ *
1794
+ * // Using index for array access
1795
+ * const foundIndex = Arr.findIndex(fruits, f => f.startsWith('c'));
1796
+ * const foundElement = Optional.isSome(foundIndex)
1797
+ * ? fruits[foundIndex.value]
1798
+ * : 'not found';
1799
+ * // 'cherry'
1800
+ *
1801
+ * // Type inference examples
1802
+ * expectType<typeof bananaIndex, Optional<SizeType.Arr>>('=');
1803
+ * expectType<typeof findNegativeIndex, (array: readonly number[]) => Optional<SizeType.Arr>>('=');
1804
+ * ```
1805
+ *
1806
+ * @see {@link find} for finding the element instead of its index
1807
+ * @see {@link indexOf} for finding elements by equality (not predicate)
1808
+ * @see {@link lastIndexOf} for finding the last occurrence
1809
+ * @see {@link Optional} for working with the returned Optional values
1810
+ */
1811
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1812
+ Arr.findIndex = ((...args) => {
1813
+ switch (args.length) {
1814
+ case 2: {
1815
+ const [array, predicate] = args;
1816
+ return pipe(array.findIndex(
1817
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1818
+ predicate)).map((idx) => (idx >= 0 ? asUint32(idx) : -1)).value;
1819
+ }
1820
+ case 1: {
1821
+ const [predicate] = args;
1822
+ return (array) => Arr.findIndex(array, predicate);
1823
+ }
1824
+ }
1825
+ });
1826
+ /**
1827
+ * Gets the index of a value in an array.
1828
+ * @param array The array to search.
1829
+ * @param searchElement The element to search for.
1830
+ * @param fromIndex The index to start searching from.
1831
+ * @returns The index if found, -1 otherwise.
1832
+ * @example
1833
+ * ```typescript
1834
+ * // Regular usage
1835
+ * const arr = ['a', 'b', 'c', 'b'];
1836
+ * const result = Arr.indexOf(arr, 'b');
1837
+ * if (Optional.isSome(result)) {
1838
+ * console.log(result.value); // 1 (branded as SizeType.Arr)
1839
+ * }
1840
+ *
1841
+ * // Curried usage for pipe composition
1842
+ * const findB = Arr.indexOf('b');
1843
+ * const result2 = pipe(['a', 'b', 'c']).map(findB).value;
1844
+ * console.log(Optional.unwrapOr(result2, -1)); // 1
1845
+ * ```
1846
+ */
1847
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1848
+ Arr.indexOf = ((...args) => {
1849
+ switch (args.length) {
1850
+ case 2: {
1851
+ const [array, searchElement] = args;
1852
+ const index = array.indexOf(searchElement);
1853
+ return index >= 0 ? asUint32(index) : -1;
1854
+ }
1855
+ case 1: {
1856
+ const [searchElement] = args;
1857
+ return (array) => Arr.indexOf(array, searchElement);
1858
+ }
1859
+ }
1860
+ });
1861
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1862
+ Arr.indexOfFrom = ((...args) => {
1863
+ switch (args.length) {
1864
+ case 3: {
1865
+ const [array, searchElement, fromIndex] = args;
1866
+ const index = array.indexOf(searchElement, fromIndex);
1867
+ return index >= 0 ? asUint32(index) : -1;
1868
+ }
1869
+ case 2: {
1870
+ const [searchElement, fromIndex] = args;
1871
+ return (array) => Arr.indexOfFrom(array, searchElement, fromIndex);
1872
+ }
1873
+ }
1874
+ });
1875
+ /**
1876
+ * Gets the last index of a value in an array.
1877
+ * @param array The array to search.
1878
+ * @param searchElement The element to search for.
1879
+ * @param fromIndex The index to start searching from (searches backwards).
1880
+ * @returns Optional.Some with the index if found, Optional.None otherwise.
1881
+ * @example
1882
+ * ```typescript
1883
+ * // Regular usage
1884
+ * const arr = ['a', 'b', 'c', 'b'];
1885
+ * const result = Arr.lastIndexOf(arr, 'b');
1886
+ * if (Optional.isSome(result)) {
1887
+ * console.log(result.value); // 3 (branded as SizeType.Arr)
1888
+ * }
1889
+ *
1890
+ * // Curried usage for pipe composition
1891
+ * const findLastB = Arr.lastIndexOf('b');
1892
+ * const result2 = pipe(['a', 'b', 'c', 'b']).map(findLastB).value;
1893
+ * console.log(Optional.unwrapOr(result2, -1)); // 3
1894
+ * ```
1895
+ */
1896
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1897
+ Arr.lastIndexOf = ((...args) => {
1898
+ switch (args.length) {
1899
+ case 2: {
1900
+ const [array, searchElement] = args;
1901
+ const index = array.lastIndexOf(searchElement);
1902
+ return index >= 0 ? asUint32(index) : -1;
1903
+ }
1904
+ case 1: {
1905
+ const [searchElement] = args;
1906
+ return (array) => Arr.lastIndexOf(array, searchElement);
1907
+ }
1908
+ }
1909
+ });
1910
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1911
+ Arr.lastIndexOfFrom = ((...args) => {
1912
+ switch (args.length) {
1913
+ case 3: {
1914
+ const [array, searchElement, fromIndex] = args;
1915
+ const index = array.lastIndexOf(searchElement, fromIndex);
1916
+ return index >= 0 ? asUint32(index) : -1;
1917
+ }
1918
+ case 2: {
1919
+ const [searchElement, fromIndex] = args;
1920
+ return (array) => Arr.lastIndexOfFrom(array, searchElement, fromIndex);
1921
+ }
1922
+ }
1923
+ });
1924
+ // reducing value
1925
+ /**
1926
+ * Applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
1927
+ * This is an alias for `Array.prototype.reduce`.
1928
+ * @template E The type of elements in the array.
1929
+ * @template S The type of the accumulated value.
1930
+ * @param array The input array.
1931
+ * @param callbackfn A function to execute on each element in the array: `(previousValue: S, currentValue: A, currentIndex: SizeType.Arr) => S`.
1932
+ * @param initialValue The initial value of the accumulator.
1933
+ * @returns The single value that results from the reduction.
1934
+ * @example
1935
+ * ```ts
1936
+ * // Regular usage
1937
+ * Arr.foldl([1, 2, 3], (sum, n) => sum + n, 0); // 6
1938
+ *
1939
+ * // Curried usage for pipe composition
1940
+ * const sumWithZero = Arr.foldl((sum: number, n: number) => sum + n, 0);
1941
+ * const result = pipe([1, 2, 3, 4]).map(sumWithZero).value;
1942
+ * console.log(result); // 10
1943
+ * ```
1944
+ */
1945
+ Arr.foldl = (...args) => {
1946
+ switch (args.length) {
1947
+ case 3: {
1948
+ const [array, callbackfn, initialValue] = args;
1949
+ return array.reduce((prev, curr, index) => callbackfn(prev, curr, asUint32(index)), initialValue);
1950
+ }
1951
+ case 2: {
1952
+ const [callbackfn, initialValue] = args;
1953
+ return (array) => Arr.foldl(array, callbackfn, initialValue);
1954
+ }
1955
+ }
1956
+ };
1957
+ /**
1958
+ * Applies a function against an accumulator and each element in the array (from right to left) to reduce it to a single value.
1959
+ * This is an alias for `Array.prototype.reduceRight`.
1960
+ * @template E The type of elements in the array.
1961
+ * @template S The type of the accumulated value.
1962
+ * @param array The input array.
1963
+ * @param callbackfn A function to execute on each element in the array: `(previousValue: S, currentValue: A, currentIndex: SizeType.Arr) => S`.
1964
+ * @param initialValue The initial value of the accumulator.
1965
+ * @returns The single value that results from the reduction.
1966
+ * @example
1967
+ * ```ts
1968
+ * // Regular usage
1969
+ * Arr.foldr([1, 2, 3], (sum, n) => sum + n, 0); // 6
1970
+ *
1971
+ * // Curried usage for pipe composition
1972
+ * const concatRight = Arr.foldr((acc: string, curr: string) => curr + acc, '');
1973
+ * const result = pipe(['a', 'b', 'c']).map(concatRight).value;
1974
+ * console.log(result); // "abc"
1975
+ * ```
1976
+ */
1977
+ Arr.foldr = (...args) => {
1978
+ switch (args.length) {
1979
+ case 3: {
1980
+ const [array, callbackfn, initialValue] = args;
1981
+ return array.reduceRight((prev, curr, index) => callbackfn(prev, curr, asUint32(index)), initialValue);
1982
+ }
1983
+ case 2: {
1984
+ const [callbackfn, initialValue] = args;
1985
+ return (array) => Arr.foldr(array, callbackfn, initialValue);
1986
+ }
1987
+ }
1988
+ };
1989
+ /**
1990
+ * Finds the minimum value in an array.
1991
+ * @template E The type of numbers in the array (must extend `number`).
1992
+ * @param array The input array.
1993
+ * @param comparator An optional custom comparator function `(x: N, y: N) => number`. Should return < 0 if x is smaller, 0 if equal, > 0 if x is larger. Defaults to `x - y`.
1994
+ * @returns The minimum value in the array wrapped in Optional.
1995
+ * @example
1996
+ * ```ts
1997
+ * Arr.min([3, 1, 4, 1, 5] as const); // Optional.some(1)
1998
+ * Arr.min([{v:3}, {v:1}], (a,b) => a.v - b.v) // Optional.some({v:1})
1999
+ * Arr.min([]); // Optional.none
2000
+ * ```
2001
+ */
2002
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2003
+ Arr.min = ((array, comparator) => {
2004
+ if (!Arr.isNonEmpty(array)) {
2005
+ return Optional.none;
2006
+ }
2007
+ const cmp = comparator ?? ((x, y) => Num.from(x) - Num.from(y));
2008
+ return Optional.some(array.reduce((currentMin, curr) => (cmp(curr, currentMin) < 0 ? curr : currentMin), array[0]));
2009
+ });
2010
+ /**
2011
+ * Finds the maximum value in an array.
2012
+ *
2013
+ * - If the array is non-empty, returns the maximum value.
2014
+ * - If the array is empty, returns `Optional.none`.
2015
+ * - You can provide a custom comparator for arbitrary types.
2016
+ *
2017
+ * @template E The type of elements in the array.
2018
+ * @param array The input array.
2019
+ * @param comparator An optional custom comparator function `(x: A, y: A) => number`. Should return < 0 if x is smaller, 0 if equal, > 0 if x is larger. Defaults to numeric comparison.
2020
+ * @returns The maximum value in the array wrapped in Optional.
2021
+ * @example
2022
+ * ```ts
2023
+ * Arr.max([3, 1, 4, 1, 5] as const); // Optional.some(5)
2024
+ * Arr.max([{v:3}, {v:1}], (a,b) => a.v - b.v) // Optional.some({v:3})
2025
+ * Arr.max([]); // Optional.none
2026
+ * ```
2027
+ */
2028
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2029
+ Arr.max = ((array, comparator) => {
2030
+ const cmp = comparator ?? ((x, y) => Num.from(x) - Num.from(y));
2031
+ // Find max by finding min with an inverted comparator
2032
+ return Arr.min(array, (x, y) => -cmp(x, y));
2033
+ });
2034
+ /**
2035
+ * Finds the element with the minimum value according to a mapped numeric value.
2036
+ *
2037
+ * - If the array is non-empty, returns the element with the minimum mapped value.
2038
+ * - If the array is empty, returns `Optional.none`.
2039
+ * - You can provide a custom comparator for the mapped values.
2040
+ *
2041
+ * @template E The type of elements in the array.
2042
+ * @template V The type of the value to compare by.
2043
+ * @param array The input array.
2044
+ * @param comparatorValueMapper A function that maps an element to a value for comparison.
2045
+ * @param comparator An optional custom comparator function for the mapped values.
2046
+ * @returns The element with the minimum mapped value wrapped in Optional.
2047
+ * @example
2048
+ * ```ts
2049
+ * const people = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 20 }] as const;
2050
+ * Arr.minBy(people, p => p.age); // Optional.some({ name: 'Bob', age: 20 })
2051
+ * Arr.minBy([], p => p.age); // Optional.none
2052
+ * ```
2053
+ */
2054
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2055
+ Arr.minBy = ((array, comparatorValueMapper, comparator) => Arr.min(array, (x, y) => comparator === undefined
2056
+ ? Num.from(comparatorValueMapper(x)) -
2057
+ Num.from(comparatorValueMapper(y))
2058
+ : comparator(comparatorValueMapper(x), comparatorValueMapper(y))));
2059
+ /**
2060
+ * Finds the element with the maximum value according to a mapped numeric value.
2061
+ *
2062
+ * - If the array is non-empty, returns the element with the maximum mapped value.
2063
+ * - If the array is empty, returns `Optional.none`.
2064
+ * - You can provide a custom comparator for the mapped values.
2065
+ *
2066
+ * @template E The type of elements in the array.
2067
+ * @template V The type of the value to compare by.
2068
+ * @param array The input array.
2069
+ * @param comparatorValueMapper A function that maps an element to a value for comparison.
2070
+ * @param comparator An optional custom comparator function for the mapped values.
2071
+ * @returns The element with the maximum mapped value wrapped in Optional.
2072
+ * @example
2073
+ * ```ts
2074
+ * const people = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 20 }] as const;
2075
+ * Arr.maxBy(people, p => p.age); // Optional.some({ name: 'Alice', age: 30 })
2076
+ * Arr.maxBy([], p => p.age); // Optional.none
2077
+ * ```
2078
+ */
2079
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2080
+ Arr.maxBy = ((array, comparatorValueMapper, comparator) => Arr.max(array, (x, y) => comparator === undefined
2081
+ ? Num.from(comparatorValueMapper(x)) -
2082
+ Num.from(comparatorValueMapper(y))
2083
+ : comparator(comparatorValueMapper(x), comparatorValueMapper(y))));
2084
+ /**
2085
+ * Counts the number of elements in an array that satisfy a predicate.
2086
+ * @template E The type of elements in the array.
2087
+ * @param array The input array.
2088
+ * @param predicate A function `(value: A, index: number) => boolean` to test each element for a condition.
2089
+ * @returns The number of elements that satisfy the predicate.
2090
+ * @example
2091
+ * ```ts
2092
+ * // Regular usage
2093
+ * Arr.count([1, 2, 3, 4], (x) => x > 2); // 2
2094
+ *
2095
+ * // Curried usage for pipe composition
2096
+ * const countEvens = Arr.count((x: number) => x % 2 === 0);
2097
+ * const result = pipe([1, 2, 3, 4, 5, 6]).map(countEvens).value;
2098
+ * console.log(result); // 3
2099
+ * ```
2100
+ */
2101
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2102
+ Arr.count = ((...args) => {
2103
+ switch (args.length) {
2104
+ case 2: {
2105
+ const [array, predicate] = args;
2106
+ return array.reduce((acc, curr, index) => predicate(curr, asUint32(index)) ? Uint32.add(acc, 1) : acc, asUint32(0));
2107
+ }
2108
+ case 1: {
2109
+ const [predicate] = args;
2110
+ return (array) => Arr.count(array, predicate);
2111
+ }
2112
+ }
2113
+ });
2114
+ /**
2115
+ * Groups elements of an array by a key derived from each element and counts the elements in each group.
2116
+ * @template E The type of elements in the array.
2117
+ * @template G The type of the group key (must be a primitive type: `string`, `number`, `boolean`, `symbol`, `bigint`, `null`, or `undefined`).
2118
+ * @param array The input array.
2119
+ * @param grouper A function `(value: A, index: number) => G` that maps an element and its index to a group key.
2120
+ * @returns An `IMap` where keys are group keys and values are the counts of elements in each group.
2121
+ * @example
2122
+ * ```ts
2123
+ * // Regular usage
2124
+ * Arr.countBy([1, 2, 2, 3, 1, 1], (x) => x);
2125
+ * // IMap { 1 => 3, 2 => 2, 3 => 1 }
2126
+ *
2127
+ * // Curried usage for pipe composition
2128
+ * const countByType = Arr.countBy((x: {type: string}) => x.type);
2129
+ * const data = [{type: 'a'}, {type: 'b'}, {type: 'a'}];
2130
+ * const result = pipe(data).map(countByType).value;
2131
+ * // IMap { 'a' => 2, 'b' => 1 }
2132
+ * ```
2133
+ */
2134
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2135
+ Arr.countBy = ((...args) => {
2136
+ switch (args.length) {
2137
+ case 2: {
2138
+ const [array, grouper] = args;
2139
+ const mut_groups = new Map();
2140
+ for (const [index, e] of array.entries()) {
2141
+ const key = grouper(e, asUint32(index));
2142
+ const curr = mut_groups.get(key) ?? 0;
2143
+ mut_groups.set(key, asUint32(curr + 1));
2144
+ }
2145
+ return IMap.create(mut_groups);
2146
+ }
2147
+ case 1: {
2148
+ const [grouper] = args;
2149
+ return (array) => Arr.countBy(array, grouper);
2150
+ }
2151
+ }
2152
+ });
2153
+ /**
2154
+ * Calculates the sum of numbers in an array.
2155
+ * @param array The input array of numbers.
2156
+ * @returns The sum of the numbers. Returns 0 for an empty array.
2157
+ * @example
2158
+ * ```ts
2159
+ * Arr.sum([1, 2, 3]); // 6
2160
+ * Arr.sum([]); // 0
2161
+ * Arr.sum([-1, 0, 1]); // 0
2162
+ * ```
2163
+ */
2164
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2165
+ Arr.sum = ((array) => array.reduce((prev, curr) => prev + curr, 0));
2166
+ /**
2167
+ * Joins array elements into a string.
2168
+ * @param array The array to join.
2169
+ * @param separator The separator string.
2170
+ * @returns Result.Ok with the joined string, Result.Err if the operation throws.
2171
+ * @example
2172
+ * ```typescript
2173
+ * // Regular usage
2174
+ * const arr = ['Hello', 'World'];
2175
+ * const result = Arr.join(arr, ' ');
2176
+ * if (Result.isOk(result)) {
2177
+ * console.log(result.value); // "Hello World"
2178
+ * }
2179
+ *
2180
+ * // Curried usage for pipe composition
2181
+ * const joinWithComma = Arr.join(',');
2182
+ * const result2 = pipe(['a', 'b', 'c']).map(joinWithComma).value;
2183
+ * console.log(Result.unwrapOr(result2, '')); // "a,b,c"
2184
+ * ```
2185
+ */
2186
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2187
+ Arr.join = ((...args) => {
2188
+ switch (args.length) {
2189
+ case 0:
2190
+ return (array) => joinImpl(array, undefined);
2191
+ case 1: {
2192
+ const [arg] = args;
2193
+ if (isString(arg) || isUndefined(arg)) {
2194
+ return (array) => joinImpl(array, arg);
2195
+ }
2196
+ return joinImpl(arg, undefined);
2197
+ }
2198
+ case 2: {
2199
+ const [array, separator] = args;
2200
+ return joinImpl(array, separator);
2201
+ }
2202
+ }
2203
+ });
2204
+ const joinImpl = (array, separator) => {
2205
+ try {
2206
+ const result = array.join(separator);
2207
+ return Result.ok(result);
2208
+ }
2209
+ catch (error) {
2210
+ return Result.err(error instanceof Error
2211
+ ? error
2212
+ : pipe(unknownToString(error))
2213
+ .map(Result.unwrapOkOr('Failed to join array'))
2214
+ .map((e) => new Error(e)).value);
2215
+ }
2216
+ };
2217
+ // transformation
2218
+ /**
2219
+ * Creates an array of tuples by pairing up corresponding elements from two arrays.
2220
+ * The resulting array has a length equal to the minimum of the two input array lengths.
2221
+ * @template E1 The type of the first array.
2222
+ * @template E2 The type of the second array.
2223
+ * @param array1 The first array.
2224
+ * @param array2 The second array.
2225
+ * @returns An array of tuples where each tuple contains corresponding elements from both arrays.
2226
+ * @example
2227
+ * ```ts
2228
+ * Arr.zip([1, 2, 3] as const, ['a', 'b', 'c'] as const); // [[1, 'a'], [2, 'b'], [3, 'c']]
2229
+ * Arr.zip([1, 2], ['a', 'b', 'c']); // [[1, 'a'], [2, 'b']]
2230
+ * Arr.zip([1, 2, 3], ['a']); // [[1, 'a']]
2231
+ * Arr.zip([], ['a']); // []
2232
+ * ```
2233
+ */
2234
+ Arr.zip = (array1, array2) =>
2235
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2236
+ Arr.seq(Uint32.min(Arr.size(array1), Arr.size(array2))).map((i) =>
2237
+ // Non-null assertion is safe here because `i` is always within bounds of both arrays up to the length of the shorter one.
2238
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2239
+ tp(array1[i], array2[i]));
2240
+ /**
2241
+ * Filters an array by excluding elements for which the predicate returns true.
2242
+ * This is the opposite of `Array.prototype.filter`.
2243
+ * @template E The type of elements in the array.
2244
+ * @param array The input array.
2245
+ * @param predicate A function `(a: A, index: number) => boolean` that returns `true` for elements to be excluded.
2246
+ * @returns A new array with elements for which the predicate returned `false`.
2247
+ * @example
2248
+ * ```ts
2249
+ * // Regular usage
2250
+ * Arr.filterNot([1, 2, 3, 4], (x) => x % 2 === 0); // [1, 3] (excludes even numbers)
2251
+ *
2252
+ * // Curried usage for pipe composition
2253
+ * const excludeEvens = Arr.filterNot((x: number) => x % 2 === 0);
2254
+ * const result = pipe([1, 2, 3, 4, 5, 6]).map(excludeEvens).value;
2255
+ * console.log(result); // [1, 3, 5]
2256
+ * ```
2257
+ */
2258
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2259
+ Arr.filterNot = ((...args) => {
2260
+ switch (args.length) {
2261
+ case 2: {
2262
+ const [array, predicate] = args;
2263
+ return array.filter((a, i) => !predicate(a, asUint32(i)));
2264
+ }
2265
+ case 1: {
2266
+ const [predicate] = args;
2267
+ return (array) => Arr.filterNot(array, predicate);
2268
+ }
2269
+ }
2270
+ });
2271
+ /**
2272
+ * Concatenates two arrays.
2273
+ * @template E1 The type of the first array (can be a tuple).
2274
+ * @template E2 The type of the second array (can be a tuple).
2275
+ * @param array1 The first array.
2276
+ * @param array2 The second array.
2277
+ * @returns A new array that is the concatenation of the two input arrays. Type is `readonly [...E1, ...E2]`.
2278
+ * @example
2279
+ * ```ts
2280
+ * Arr.concat([1, 2] as const, [3, 4] as const); // [1, 2, 3, 4]
2281
+ * Arr.concat([], [1, 2]); // [1, 2]
2282
+ * Arr.concat([1, 2], []); // [1, 2]
2283
+ * ```
2284
+ */
2285
+ Arr.concat = (array1, array2) => [...array1, ...array2];
2286
+ /**
2287
+ * Partitions an array into sub-arrays of a specified size.
2288
+ * The last partition may be smaller if the array length is not a multiple of `chunkSize`.
2289
+ * Returns an empty array if chunkSize < 2.
2290
+ * @template N The size of each partition (must be a number type, typically a literal for precise typing).
2291
+ * @template E The type of elements in the array.
2292
+ * @param array The input array.
2293
+ * @param chunkSize The size of each partition.
2294
+ * @returns An array of arrays, where each inner array has up to `chunkSize` elements.
2295
+ * @example
2296
+ * ```ts
2297
+ * // Regular usage
2298
+ * Arr.partition([1, 2, 3, 4, 5, 6], 2); // [[1, 2], [3, 4], [5, 6]]
2299
+ *
2300
+ * // Curried usage for pipe composition
2301
+ * const chunkBy3 = Arr.partition(3);
2302
+ * const result = pipe([1, 2, 3, 4, 5, 6, 7]).map(chunkBy3).value;
2303
+ * console.log(result); // [[1, 2, 3], [4, 5, 6], [7]]
2304
+ * ```
2305
+ */
2306
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2307
+ Arr.partition = ((...args) => {
2308
+ switch (args.length) {
2309
+ case 2: {
2310
+ const [array, chunkSize] = args;
2311
+ return chunkSize < 2
2312
+ ? []
2313
+ : Arr.seq(asUint32(Math.ceil(array.length / chunkSize))).map((i) => array.slice(chunkSize * i, chunkSize * (i + 1)));
2314
+ }
2315
+ case 1: {
2316
+ const [chunkSize] = args;
2317
+ return (array) => Arr.partition(array, chunkSize);
2318
+ }
2319
+ }
2320
+ });
2321
+ /**
2322
+ * Sorts an array by a value derived from its elements, using a numeric mapping.
2323
+ * @template E The type of elements in the array.
2324
+ * @param array The input array.
2325
+ * @param comparatorValueMapper A function `(value: A) => number` that maps an element to a number for comparison.
2326
+ * @param comparator An optional custom comparator function `(x: number, y: number) => number` for the mapped numbers. Defaults to ascending sort (x - y).
2327
+ * @returns A new array sorted by the mapped values.
2328
+ * @example
2329
+ * ```ts
2330
+ * const items = [{ name: 'Eve', score: 70 }, { name: 'Adam', score: 90 }, { name: 'Bob', score: 80 }];
2331
+ * Arr.toSortedBy(items, item => item.score);
2332
+ * // [{ name: 'Eve', score: 70 }, { name: 'Bob', score: 80 }, { name: 'Adam', score: 90 }]
2333
+ * Arr.toSortedBy(items, item => item.score, (a, b) => b - a); // Sort descending
2334
+ * // [{ name: 'Adam', score: 90 }, { name: 'Bob', score: 80 }, { name: 'Eve', score: 70 }]
2335
+ * ```
2336
+ */
2337
+ Arr.toSortedBy = (array, comparatorValueMapper, comparator) => array.toSorted((x, y) => comparator === undefined
2338
+ ? // This branch assumes B is number if comparator is undefined.
2339
+ // The overloads should handle this, but explicit cast might be needed if B is not number.
2340
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2341
+ comparatorValueMapper(x) -
2342
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2343
+ comparatorValueMapper(y)
2344
+ : comparator(comparatorValueMapper(x), comparatorValueMapper(y)));
2345
+ /**
2346
+ * Returns an array of successively reduced values from an array, starting with an initial value.
2347
+ *
2348
+ * This function creates a \"running tally\" by applying a reducer function to each element and
2349
+ * accumulating the results. Unlike {@link reduce} which returns a single final value, `scan`
2350
+ * returns all intermediate accumulated values, providing visibility into the reduction process.
2351
+ *
2352
+ * **Key Differences from Reduce:**
2353
+ * - {@link reduce}: `[1, 2, 3] -> 6` (final sum only)
2354
+ * - `scan`: `[1, 2, 3] -> [0, 1, 3, 6]` (all intermediate sums including initial value)
2355
+ *
2356
+ * **Guaranteed Non-Empty Return:** The result is always a {@link NonEmptyArray}<S> because it includes
2357
+ * the initial value as the first element, even for empty input arrays. This provides type safety
2358
+ * and eliminates the need for empty array checks.
2359
+ *
2360
+ * **Array Length Relationship:** `result.length === array.length + 1` (includes initial value)
2361
+ *
2362
+ * **Curried Usage:** Supports currying for functional composition - when called with only the reducer
2363
+ * and initial value, returns a reusable function that can be applied to arrays.
2364
+ *
2365
+ * @template E The type of elements in the input array.
2366
+ * @template S The type of the accumulated values and the initial value.
2367
+ * @param array The input array to scan over. Can be empty (result will still contain the initial value).
2368
+ * @param reducer A function `(accumulator: S, currentValue: E, currentIndex: SizeType.Arr) => S` that:
2369
+ * - **accumulator:** The current accumulated value (starts with `init`, then previous results)
2370
+ * - **currentValue:** The current array element being processed
2371
+ * - **currentIndex:** The 0-based index of the current element (typed as {@link SizeType.Arr})
2372
+ * - **returns:** The new accumulated value to include in the result array
2373
+ * @param init The initial accumulated value. Becomes the first element of the result array.
2374
+ * @returns A {@link NonEmptyArray}<S> of accumulated values with length `array.length + 1`:
2375
+ * - `result[0]` is always the `init` value
2376
+ * - `result[i+1]` is the result of applying the reducer to `result[i]` and `array[i]`
2377
+ * - Guaranteed to be non-empty regardless of input array length
2378
+ *
2379
+ * @example
2380
+ * ```typescript
2381
+ * // Basic running sum example
2382
+ * const numbers = [1, 2, 3, 4];
2383
+ * const runningSum = Arr.scan(numbers, (acc, curr) => acc + curr, 0);
2384
+ * // NonEmptyArray<number> -> [0, 1, 3, 6, 10]
2385
+ * // ^ ^ ^ ^ ^
2386
+ * // | | | | └─ 0+1+2+3+4 = 10
2387
+ * // | | | └─ 0+1+2+3 = 6
2388
+ * // | | └─ 0+1+2 = 3
2389
+ * // | └─ 0+1 = 1
2390
+ * // └─ init = 0
2391
+ *
2392
+ * // Difference from reduce
2393
+ * const reduced = numbers.reduce((acc, curr) => acc + curr, 0); // 10 (final only)
2394
+ * const scanned = Arr.scan(numbers, (acc, curr) => acc + curr, 0); // [0, 1, 3, 6, 10] (all steps)
2395
+ *
2396
+ * // Running product
2397
+ * const factorial = Arr.scan([1, 2, 3, 4, 5], (acc, curr) => acc * curr, 1);
2398
+ * // [1, 1, 2, 6, 24, 120] - factorial sequence
2399
+ *
2400
+ * // Running maximum
2401
+ * const temperatures = [20, 25, 18, 30, 22];
2402
+ * const runningMax = Arr.scan(temperatures, (max, temp) => Math.max(max, temp), -Infinity);
2403
+ * // [-Infinity, 20, 25, 25, 30, 30]
2404
+ *
2405
+ * // Building strings incrementally
2406
+ * const words = ['Hello', 'beautiful', 'world'];
2407
+ * const sentences = Arr.scan(words, (sentence, word) => sentence + ' ' + word, '');
2408
+ * // ['', ' Hello', ' Hello beautiful', ' Hello beautiful world']
2409
+ *
2410
+ * // Array accumulation (collecting elements)
2411
+ * const items = ['a', 'b', 'c'];
2412
+ * const growing = Arr.scan(items, (acc, item) => [...acc, item], [] as string[]);
2413
+ * // [[], ['a'], ['a', 'b'], ['a', 'b', 'c']]
2414
+ *
2415
+ * // Financial running balance
2416
+ * const transactions = [100, -20, 50, -30];
2417
+ * const balances = Arr.scan(transactions, (balance, transaction) => balance + transaction, 1000);
2418
+ * // [1000, 1100, 1080, 1130, 1100] - account balance after each transaction
2419
+ *
2420
+ * // Using index information
2421
+ * const letters = ['a', 'b', 'c'];
2422
+ * const indexed = Arr.scan(letters, (acc, letter, index) => acc + `${index}:${letter} `, '');
2423
+ * // ['', '0:a ', '0:a 1:b ', '0:a 1:b 2:c ']
2424
+ *
2425
+ * // Edge cases
2426
+ * const emptyArray: number[] = [];
2427
+ * const emptyResult = Arr.scan(emptyArray, (acc, curr) => acc + curr, 42);
2428
+ * // [42] - NonEmptyArray even for empty input
2429
+ *
2430
+ * const singleElement = Arr.scan([5], (acc, curr) => acc * curr, 2);
2431
+ * // [2, 10] - init value plus one result
2432
+ *
2433
+ * // Complex object accumulation
2434
+ * const sales = [
2435
+ * { product: 'A', amount: 100 },
2436
+ * { product: 'B', amount: 200 },
2437
+ * { product: 'A', amount: 150 }
2438
+ * ];
2439
+ *
2440
+ * const runningSales = Arr.scan(sales, (totals, sale) => ({
2441
+ * ...totals,
2442
+ * [sale.product]: (totals[sale.product] || 0) + sale.amount
2443
+ * }), {} as Record<string, number>);
2444
+ * // [
2445
+ * // {},
2446
+ * // { A: 100 },
2447
+ * // { A: 100, B: 200 },
2448
+ * // { A: 250, B: 200 }
2449
+ * // ]
2450
+ *
2451
+ * // Curried usage for functional composition
2452
+ * const runningSumFn = Arr.scan((acc: number, curr: number) => acc + curr, 0);
2453
+ * const runningProductFn = Arr.scan((acc: number, curr: number) => acc * curr, 1);
2454
+ * const collectingFn = Arr.scan((acc: string[], curr: string) => [...acc, curr], [] as string[]);
2455
+ *
2456
+ * const datasets = [[1, 2, 3], [4, 5], [6, 7, 8, 9]];
2457
+ * const allSums = datasets.map(runningSumFn);
2458
+ * // [
2459
+ * // [0, 1, 3, 6],
2460
+ * // [0, 4, 9],
2461
+ * // [0, 6, 13, 21, 30]
2462
+ * // ]
2463
+ *
2464
+ * // Pipe composition for data analysis
2465
+ * const analysisResult = pipe([10, 20, 30, 40])
2466
+ * .map(runningSumFn)
2467
+ * .map(sums => sums.slice(1)) // Remove initial value to get pure running sums
2468
+ * .map(sums => sums.map((sum, i) => ({ step: i + 1, total: sum })))
2469
+ * .value;
2470
+ * // [{ step: 1, total: 10 }, { step: 2, total: 30 }, { step: 3, total: 60 }, { step: 4, total: 100 }]
2471
+ *
2472
+ * // Advanced: State machine simulation
2473
+ * type State = 'idle' | 'loading' | 'success' | 'error';
2474
+ * type Event = 'start' | 'complete' | 'fail' | 'reset';
2475
+ *
2476
+ * const events: Event[] = ['start', 'complete', 'reset', 'start', 'fail'];
2477
+ * const stateTransition = (state: State, event: Event): State => {
2478
+ * switch (state) {
2479
+ * case 'idle': return event === 'start' ? 'loading' : state;
2480
+ * case 'loading': return event === 'complete' ? 'success' : event === 'fail' ? 'error' : state;
2481
+ * case 'success': return event === 'reset' ? 'idle' : state;
2482
+ * case 'error': return event === 'reset' ? 'idle' : state;
2483
+ * }
2484
+ * };
2485
+ *
2486
+ * const stateHistory = Arr.scan(events, stateTransition, 'idle' as State);
2487
+ * // ['idle', 'loading', 'success', 'idle', 'loading', 'error']
2488
+ *
2489
+ * // Type inference examples
2490
+ * expectType<typeof runningSum, NonEmptyArray<number>>('=');
2491
+ * expectType<typeof emptyResult, NonEmptyArray<number>>('=');
2492
+ * expectType<typeof runningSumFn, <T extends readonly number[]>(array: T) => NonEmptyArray<number>>('=');
2493
+ * expectType<typeof stateHistory, NonEmptyArray<State>>('=');
2494
+ * ```
2495
+ *
2496
+ * @see {@link reduce} for getting only the final accumulated value
2497
+ * @see {@link NonEmptyArray} for understanding the guaranteed non-empty return type
2498
+ * @see {@link SizeType.Arr} for the index parameter type
2499
+ * @see Array.prototype.reduce for the standard reduce function
2500
+ */
2501
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2502
+ Arr.scan = ((...args) => {
2503
+ switch (args.length) {
2504
+ case 3: {
2505
+ const [array, reducer, init] = args;
2506
+ const mut_result = castMutable(Arr.newArray(asPositiveUint32(array.length + 1), init));
2507
+ let mut_acc = init;
2508
+ for (const [index, value] of array.entries()) {
2509
+ mut_acc = reducer(mut_acc, value, asUint32(index));
2510
+ mut_result[index + 1] = mut_acc;
2511
+ }
2512
+ return mut_result;
2513
+ }
2514
+ case 2: {
2515
+ const [reducer, init] = args;
2516
+ return (array) => Arr.scan(array, reducer, init);
2517
+ }
2518
+ }
2519
+ });
2520
+ /**
2521
+ * Groups elements of an array by a key derived from each element, returning an immutable {@link IMap}.
2522
+ *
2523
+ * This function categorizes array elements into groups based on a computed key, using the efficient
2524
+ * {@link IMap} data structure for the result. The grouper function receives both the element and its
2525
+ * index, enabling flexible grouping strategies.
2526
+ *
2527
+ * **MapSetKeyType Constraint:** The group key type `G` must extend {@link MapSetKeyType}, which includes
2528
+ * primitive types that can be used as Map keys (string, number, boolean, symbol, null, undefined).
2529
+ * This constraint ensures type safety and efficient key-based operations.
2530
+ *
2531
+ * **IMap Return Type:** Returns an {@link IMap}<G, readonly E[]> where:
2532
+ * - Keys are the computed group identifiers of type `G`
2533
+ * - Values are immutable arrays containing all elements that belong to each group
2534
+ * - Preserves insertion order of first occurrence of each group
2535
+ * - Maintains type safety with precise generic types
2536
+ *
2537
+ * **Curried Usage:** Supports currying for functional composition - when called with only the grouper
2538
+ * function, returns a reusable function that can be applied to arrays.
2539
+ *
2540
+ * @template E The type of elements in the input array.
2541
+ * @template G The type of the group key, constrained to {@link MapSetKeyType} (primitives usable as Map keys).
2542
+ * Must be one of: `string | number | boolean | symbol | null | undefined`
2543
+ * @param array The input array to group. Can be empty (returns empty {@link IMap}).
2544
+ * @param grouper A function `(value: E, index: SizeType.Arr) => G` that computes the group key for each element.
2545
+ * - **value:** The current array element
2546
+ * - **index:** The 0-based index of the element (typed as {@link SizeType.Arr})
2547
+ * - **returns:** The group key (must be {@link MapSetKeyType})
2548
+ * @returns An {@link IMap}<G, readonly E[]> where:
2549
+ * - Keys are unique group identifiers computed by the grouper function
2550
+ * - Values are immutable arrays of elements belonging to each group
2551
+ * - Empty groups are not included (only groups with at least one element)
2552
+ * - Insertion order is preserved based on first occurrence of each group key
2553
+ *
2554
+ * @example
2555
+ * ```typescript
2556
+ * // Basic grouping by object property
2557
+ * const products = [
2558
+ * { type: 'fruit', name: 'apple', price: 1.2 },
2559
+ * { type: 'vegetable', name: 'carrot', price: 0.8 },
2560
+ * { type: 'fruit', name: 'banana', price: 0.9 },
2561
+ * { type: 'vegetable', name: 'broccoli', price: 2.1 },
2562
+ * { type: 'fruit', name: 'orange', price: 1.5 }
2563
+ * ];
2564
+ *
2565
+ * const byType = Arr.groupBy(products, item => item.type);
2566
+ * // IMap<string, readonly Product[]> {
2567
+ * // 'fruit' => [
2568
+ * // { type: 'fruit', name: 'apple', price: 1.2 },
2569
+ * // { type: 'fruit', name: 'banana', price: 0.9 },
2570
+ * // { type: 'fruit', name: 'orange', price: 1.5 }
2571
+ * // ],
2572
+ * // 'vegetable' => [
2573
+ * // { type: 'vegetable', name: 'carrot', price: 0.8 },
2574
+ * // { type: 'vegetable', name: 'broccoli', price: 2.1 }
2575
+ * // ]
2576
+ * // }
2577
+ *
2578
+ * // Access grouped results with IMap methods
2579
+ * const fruits = IMap.get(byType, 'fruit'); // Optional<readonly Product[]>
2580
+ * const fruitCount = Optional.map(fruits, arr => arr.length); // Optional<number>
2581
+ * const fruitNames = Optional.map(fruits, arr => arr.map(p => p.name)); // Optional<string[]>
2582
+ *
2583
+ * // Grouping by computed values
2584
+ * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
2585
+ * const byParity = Arr.groupBy(numbers, n => n % 2 === 0 ? 'even' : 'odd');
2586
+ * // IMap<string, readonly number[]> {
2587
+ * // 'odd' => [1, 3, 5, 7, 9],
2588
+ * // 'even' => [2, 4, 6, 8, 10]
2589
+ * // }
2590
+ *
2591
+ * // Grouping by price ranges using index information
2592
+ * const byPriceRange = Arr.groupBy(products, (product, index) => {
2593
+ * const category = product.price < 1.0 ? 'cheap' :
2594
+ * product.price < 2.0 ? 'moderate' : 'expensive';
2595
+ * return `${category}_${index < 2 ? 'early' : 'late'}`;
2596
+ * });
2597
+ *
2598
+ * // MapSetKeyType constraint examples (valid key types)
2599
+ * const byStringKey = Arr.groupBy([1, 2, 3], n => `group_${n}`); // string keys
2600
+ * const byNumberKey = Arr.groupBy(['a', 'b', 'c'], (_, i) => i); // number keys
2601
+ * const byBooleanKey = Arr.groupBy([1, 2, 3, 4], n => n > 2); // boolean keys
2602
+ * const bySymbolKey = Arr.groupBy([1, 2], n => Symbol(n.toString())); // symbol keys
2603
+ *
2604
+ * // Edge cases
2605
+ * const emptyGroup = Arr.groupBy([], x => x); // IMap<never, readonly never[]> (empty)
2606
+ * const singleGroup = Arr.groupBy([1, 2, 3], () => 'all'); // All elements in one group
2607
+ * const uniqueGroups = Arr.groupBy([1, 2, 3], x => x); // Each element in its own group
2608
+ *
2609
+ * // Curried usage for functional composition
2610
+ * const groupByType = Arr.groupBy((item: { type: string }) => item.type);
2611
+ * const groupByLength = Arr.groupBy((str: string) => str.length);
2612
+ * const groupByFirstChar = Arr.groupBy((str: string) => str.charAt(0).toLowerCase());
2613
+ *
2614
+ * const datasets = [
2615
+ * [{ type: 'A' }, { type: 'B' }, { type: 'A' }],
2616
+ * [{ type: 'C' }, { type: 'A' }],
2617
+ * [{ type: 'B' }, { type: 'B' }, { type: 'C' }]
2618
+ * ];
2619
+ * const allGrouped = datasets.map(groupByType);
2620
+ * // Array of IMap instances, each grouped by type
2621
+ *
2622
+ * // Pipe composition for complex data processing
2623
+ * const words = ['apple', 'banana', 'apricot', 'blueberry', 'avocado', 'blackberry'];
2624
+ * const processedGroups = pipe(words)
2625
+ * .map(groupByFirstChar)
2626
+ * .map(groupMap => IMap.map(groupMap, (wordsInGroup, firstLetter) => ({
2627
+ * letter: firstLetter,
2628
+ * count: wordsInGroup.length,
2629
+ * longest: wordsInGroup.reduce((longest, word) =>
2630
+ * word.length > longest.length ? word : longest
2631
+ * )
2632
+ * })))
2633
+ * .value;
2634
+ * // IMap<string, {letter: string, count: number, longest: string}>
2635
+ *
2636
+ * // Advanced: Grouping with complex transformations
2637
+ * const students = [
2638
+ * { name: 'Alice', grade: 85, subject: 'Math' },
2639
+ * { name: 'Bob', grade: 92, subject: 'Science' },
2640
+ * { name: 'Charlie', grade: 78, subject: 'Math' },
2641
+ * { name: 'Diana', grade: 96, subject: 'Science' }
2642
+ * ];
2643
+ *
2644
+ * const byGradeLevel = Arr.groupBy(students, student => {
2645
+ * if (student.grade >= 90) return 'A';
2646
+ * if (student.grade >= 80) return 'B';
2647
+ * return 'C';
2648
+ * });
2649
+ *
2650
+ * // Working with the grouped results
2651
+ * const aStudents = Optional.unwrapOr(IMap.get(byGradeLevel, 'A'), []);
2652
+ * const averageAGrade = aStudents.length > 0
2653
+ * ? aStudents.reduce((sum, s) => sum + s.grade, 0) / aStudents.length
2654
+ * : 0;
2655
+ *
2656
+ * // Type inference examples
2657
+ * expectType<typeof byType, IMap<string, readonly typeof products[number][]>>('=');
2658
+ * expectType<typeof byParity, IMap<string, readonly number[]>>('=');
2659
+ * expectType<typeof groupByType, <T extends {type: string}>(array: readonly T[]) => IMap<string, readonly T[]>>('=');
2660
+ * expectType<typeof emptyGroup, IMap<never, readonly never[]>>('=');
2661
+ * ```
2662
+ *
2663
+ * @see {@link IMap} for working with the returned immutable map
2664
+ * @see {@link MapSetKeyType} for understanding valid key types
2665
+ * @see {@link IMap.get} for safely accessing grouped results
2666
+ * @see {@link IMap.map} for transforming grouped data
2667
+ * @see {@link Optional} for handling potentially missing groups
2668
+ */
2669
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2670
+ Arr.groupBy = ((...args) => {
2671
+ switch (args.length) {
2672
+ case 2: {
2673
+ const [array, grouper] = args;
2674
+ const mut_groups = new Map(); // Store mutable arrays internally
2675
+ for (const [index, e] of array.entries()) {
2676
+ const key = grouper(e, asUint32(index)); // Ensure index is treated as SizeType.Arr
2677
+ const mut_group = mut_groups.get(key);
2678
+ if (mut_group !== undefined) {
2679
+ mut_group.push(e);
2680
+ }
2681
+ else {
2682
+ mut_groups.set(key, [e]);
2683
+ }
2684
+ }
2685
+ // Cast to IMap<G, readonly A[]> for the public interface
2686
+ return IMap.create(mut_groups);
2687
+ }
2688
+ case 1: {
2689
+ const [grouper] = args;
2690
+ return (array) => Arr.groupBy(array, grouper);
2691
+ }
2692
+ }
2693
+ });
2694
+ /**
2695
+ * Creates a new array with unique elements from the input array. Order is preserved from the first occurrence.
2696
+ * Uses `Set` internally for efficient uniqueness checking.
2697
+ * @template P The type of elements in the array.
2698
+ * @param array The input array.
2699
+ * @returns A new array with unique elements from the input array. Returns `[]` for an empty input.
2700
+ * @example
2701
+ * ```ts
2702
+ * Arr.uniq([1, 2, 2, 3, 1, 4]); // [1, 2, 3, 4]
2703
+ * Arr.uniq(['a', 'b', 'a']); // ['a', 'b']
2704
+ * ```
2705
+ */
2706
+ Arr.uniq = (array) => Array.from(new Set(array));
2707
+ /**
2708
+ * Creates a new array with unique elements from the input array, based on the values returned by `mapFn`.
2709
+ *
2710
+ * - If the input is a non-empty array, returns a non-empty array.
2711
+ * - Otherwise, returns a readonly array.
2712
+ *
2713
+ * @template E The type of elements in the array.
2714
+ * @template P The type of the mapped value (used for uniqueness comparison).
2715
+ * @param array The input array.
2716
+ * @param mapFn A function `(value: A) => P` to map elements to values for uniqueness comparison.
2717
+ * @returns A new array with unique elements based on the mapped values.
2718
+ * @example
2719
+ * ```ts
2720
+ * const users = [
2721
+ * { id: 1, name: 'Alice' },
2722
+ * { id: 2, name: 'Bob' },
2723
+ * { id: 1, name: 'Alicia' }, // Duplicate id
2724
+ * ];
2725
+ * Arr.uniqBy(users, user => user.id); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
2726
+ * ```
2727
+ */
2728
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2729
+ Arr.uniqBy = ((array, mapFn) => {
2730
+ const mut_mappedValues = new Set();
2731
+ return array.filter((val) => {
2732
+ const mappedValue = mapFn(val);
2733
+ if (mut_mappedValues.has(mappedValue))
2734
+ return false;
2735
+ mut_mappedValues.add(mappedValue);
2736
+ return true;
2737
+ });
2738
+ });
2739
+ // set operations & equality
2740
+ /**
2741
+ * Checks if two arrays are equal by performing a shallow comparison of their elements.
2742
+ * @template E The type of elements in the arrays.
2743
+ * @param array1 The first array.
2744
+ * @param array2 The second array.
2745
+ * @param equality An optional function `(a: T, b: T) => boolean` to compare elements. Defaults to `Object.is`.
2746
+ * @returns `true` if the arrays have the same length and all corresponding elements are equal according to the `equality` function, `false` otherwise.
2747
+ * @example
2748
+ * ```ts
2749
+ * Arr.eq([1, 2, 3], [1, 2, 3]); // true
2750
+ * Arr.eq([1, 2, 3], [1, 2, 4]); // false
2751
+ * Arr.eq([1, 2], [1, 2, 3]); // false
2752
+ * Arr.eq([{a:1}], [{a:1}]); // false (different object references)
2753
+ * Arr.eq([{a:1}], [{a:1}], (o1, o2) => o1.a === o2.a); // true
2754
+ * ```
2755
+ */
2756
+ Arr.eq = (array1, array2, equality = Object.is) => array1.length === array2.length &&
2757
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2758
+ array1.every((v, i) => equality(v, array2[i]));
2759
+ /**
2760
+ * Alias for `eq`.
2761
+ */
2762
+ Arr.equal = Arr.eq;
2763
+ /**
2764
+ * Checks if the first array (`array1`) is a subset of the second array (`array2`).
2765
+ * An array `A` is a subset of `B` if all elements of `A` are also present in `B`.
2766
+ * Elements must be primitive types for `includes` to work reliably for comparison.
2767
+ * @template E1 The type of elements in the first array (subset candidate), must be a primitive type.
2768
+ * @template E2 The type of elements in the second array (superset candidate), must be a primitive type.
2769
+ * @param array1 The first array.
2770
+ * @param array2 The second array.
2771
+ * @returns `true` if `array1` is a subset of `array2`, `false` otherwise.
2772
+ * @remarks `array1` ⊂ `array2`
2773
+ * @example
2774
+ * ```ts
2775
+ * Arr.isSubset([1, 2], [1, 2, 3]); // true
2776
+ * Arr.isSubset([1, 2, 3], [1, 2]); // false
2777
+ * Arr.isSubset([], [1, 2, 3]); // true
2778
+ * Arr.isSubset([1, 5], [1, 2, 3]); // false
2779
+ * ```
2780
+ */
2781
+ Arr.isSubset = (array1, array2) => array1.every((a) =>
2782
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2783
+ array2.includes(a));
2784
+ /**
2785
+ * Checks if the first array (`array1`) is a superset of the second array (`array2`).
2786
+ * An array `A` is a superset of `B` if all elements of `B` are also present in `A`.
2787
+ * Elements must be primitive types.
2788
+ * @template E1 The type of elements in the first array (superset candidate), must be a primitive type.
2789
+ * @template E2 The type of elements in the second array (subset candidate), must be a primitive type.
2790
+ * @param array1 The first array.
2791
+ * @param array2 The second array.
2792
+ * @returns `true` if `array1` is a superset of `array2`, `false` otherwise.
2793
+ * @remarks `array1` ⊃ `array2`
2794
+ * @example
2795
+ * ```ts
2796
+ * Arr.isSuperset([1, 2, 3], [1, 2]); // true
2797
+ * Arr.isSuperset([1, 2], [1, 2, 3]); // false
2798
+ * Arr.isSuperset([1, 2, 3], []); // true
2799
+ * ```
2800
+ */
2801
+ Arr.isSuperset = (array1, array2) => Arr.isSubset(array2, array1);
2802
+ /**
2803
+ * Returns the intersection of two arrays of primitive types.
2804
+ * The intersection contains elements that are present in both arrays. Order is based on `array1`.
2805
+ * @template E1 The type of elements in the first array (must be a primitive type).
2806
+ * @template E2 The type of elements in the second array (must be a primitive type).
2807
+ * @param array1 The first array.
2808
+ * @param array2 The second array.
2809
+ * @returns A new array containing elements that are in both `array1` and `array2`.
2810
+ * @example
2811
+ * ```ts
2812
+ * Arr.setIntersection([1, 2, 3], [2, 3, 4]); // [2, 3]
2813
+ * Arr.setIntersection(['a', 'b'], ['b', 'c']); // ['b']
2814
+ * Arr.setIntersection([1, 2], [3, 4]); // []
2815
+ * ```
2816
+ */
2817
+ Arr.setIntersection = (array1, array2) =>
2818
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
2819
+ array1.filter((e) => array2.includes(e));
2820
+ /**
2821
+ * Returns the set difference of two arrays (`array1` - `array2`).
2822
+ * The difference contains elements that are in `array1` but not in `array2`. Order is based on `array1`.
2823
+ * Elements must be primitive types.
2824
+ * @template E The type of elements in the arrays (must be a primitive type).
2825
+ * @param array1 The first array.
2826
+ * @param array2 The second array.
2827
+ * @returns A new array containing elements from `array1` that are not in `array2`.
2828
+ * @example
2829
+ * ```ts
2830
+ * Arr.setDifference([1, 2, 3], [2, 3, 4]); // [1]
2831
+ * Arr.setDifference([1, 2, 3], [1, 2, 3]); // []
2832
+ * Arr.setDifference([1, 2], [3, 4]); // [1, 2]
2833
+ * ```
2834
+ */
2835
+ Arr.setDifference = (array1, array2) => array1.filter((e) => !array2.includes(e));
2836
+ /**
2837
+ * Returns the set difference of two sorted arrays of numbers (`sortedList1` - `sortedList2`).
2838
+ * This operation is more efficient for sorted arrays than the generic `setDifference`.
2839
+ * The resulting array is also sorted.
2840
+ * @template E The type of numbers in the arrays (must extend `number`).
2841
+ * @param sortedList1 The first sorted array of numbers.
2842
+ * @param sortedList2 The second sorted array of numbers.
2843
+ * @returns A new sorted array containing numbers from `sortedList1` that are not in `sortedList2`.
2844
+ * @example
2845
+ * ```ts
2846
+ * Arr.sortedNumSetDifference([1, 2, 3, 5], [2, 4, 5]); // [1, 3]
2847
+ * Arr.sortedNumSetDifference([1, 2, 3], [1, 2, 3]); // []
2848
+ * Arr.sortedNumSetDifference([1, 2], [3, 4]); // [1, 2]
2849
+ * ```
2850
+ */
2851
+ Arr.sortedNumSetDifference = (sortedList1, sortedList2) => {
2852
+ const mut_result = [];
2853
+ let mut_it1 = 0; // iterator for sortedList1
2854
+ let mut_it2 = 0; // iterator for sortedList2
2855
+ while (mut_it1 < sortedList1.length && mut_it2 < sortedList2.length) {
2856
+ // Non-null assertions are safe due to loop condition
2857
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2858
+ const val1 = sortedList1[mut_it1];
2859
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2860
+ const val2 = sortedList2[mut_it2];
2861
+ if (val1 === val2) {
2862
+ mut_it1 += 1;
2863
+ mut_it2 += 1;
2864
+ }
2865
+ else if (val1 < val2) {
2866
+ mut_result.push(val1);
2867
+ mut_it1 += 1;
2868
+ }
2869
+ else {
2870
+ // val1 > val2
2871
+ mut_it2 += 1;
2872
+ }
2873
+ }
2874
+ // Add remaining elements from sortedList1
2875
+ for (; mut_it1 < sortedList1.length; mut_it1 += 1) {
2876
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2877
+ mut_result.push(sortedList1[mut_it1]);
2878
+ }
2879
+ return mut_result;
2880
+ };
2881
+ // aliases
2882
+ /**
2883
+ * Alias for `head`. Returns the first element of an array.
2884
+ * @see {@link head}
2885
+ */
2886
+ Arr.first = Arr.head;
2887
+ /**
2888
+ * Alias for `tail`. Returns all elements of an array except the first one.
2889
+ * @see {@link tail}
2890
+ */
2891
+ Arr.rest = Arr.tail;
2892
+ /**
2893
+ * Alias for `skip`. Skips the first N elements of an array.
2894
+ * @see {@link skip}
2895
+ */
2896
+ Arr.drop = Arr.skip;
2897
+ /**
2898
+ * Alias for `foldl`. Applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
2899
+ * @see {@link foldl}
2900
+ */
2901
+ Arr.reduce = Arr.foldl;
2902
+ /**
2903
+ * Alias for `foldr`. Applies a function against an accumulator and each element in the array (from right to left) to reduce it to a single value.
2904
+ * @see {@link foldr}
2905
+ */
2906
+ Arr.reduceRight = Arr.foldr;
2907
+ /**
2908
+ * Alias for `partition`. Splits an array into chunks of a specified size.
2909
+ * @see {@link partition}
2910
+ */
2911
+ Arr.chunk = Arr.partition;
2912
+ })(Arr || (Arr = {}));
2913
+
2914
+ export { Arr };
2915
+ //# sourceMappingURL=array-utils.mjs.map