@fluixi/utils 1.0.0-alpha.53

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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/index.cjs +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/lib/array/advanced.cjs +1 -0
  9. package/dist/lib/array/advanced.d.ts +1116 -0
  10. package/dist/lib/array/advanced.d.ts.map +1 -0
  11. package/dist/lib/array/advanced.js +2429 -0
  12. package/dist/lib/array/advanced.mjs +1 -0
  13. package/dist/lib/array/array.cjs +1 -0
  14. package/dist/lib/array/array.d.ts +6 -0
  15. package/dist/lib/array/array.d.ts.map +1 -0
  16. package/dist/lib/array/array.js +31 -0
  17. package/dist/lib/array/array.mjs +1 -0
  18. package/dist/lib/array/index.cjs +1 -0
  19. package/dist/lib/array/index.d.ts +4 -0
  20. package/dist/lib/array/index.d.ts.map +1 -0
  21. package/dist/lib/array/index.js +3 -0
  22. package/dist/lib/array/index.mjs +1 -0
  23. package/dist/lib/array/utils.cjs +1 -0
  24. package/dist/lib/array/utils.d.ts +100 -0
  25. package/dist/lib/array/utils.d.ts.map +1 -0
  26. package/dist/lib/array/utils.js +165 -0
  27. package/dist/lib/array/utils.mjs +1 -0
  28. package/dist/lib/color/color.cjs +1 -0
  29. package/dist/lib/color/color.d.ts +366 -0
  30. package/dist/lib/color/color.d.ts.map +1 -0
  31. package/dist/lib/color/color.js +722 -0
  32. package/dist/lib/color/color.mjs +1 -0
  33. package/dist/lib/color/constants.cjs +1 -0
  34. package/dist/lib/color/constants.d.ts +361 -0
  35. package/dist/lib/color/constants.d.ts.map +1 -0
  36. package/dist/lib/color/constants.js +358 -0
  37. package/dist/lib/color/constants.mjs +1 -0
  38. package/dist/lib/color/conversions.cjs +1 -0
  39. package/dist/lib/color/conversions.d.ts +166 -0
  40. package/dist/lib/color/conversions.d.ts.map +1 -0
  41. package/dist/lib/color/conversions.js +766 -0
  42. package/dist/lib/color/conversions.mjs +1 -0
  43. package/dist/lib/color/helpers.cjs +1 -0
  44. package/dist/lib/color/helpers.d.ts +75 -0
  45. package/dist/lib/color/helpers.d.ts.map +1 -0
  46. package/dist/lib/color/helpers.js +419 -0
  47. package/dist/lib/color/helpers.mjs +1 -0
  48. package/dist/lib/color/index.cjs +1 -0
  49. package/dist/lib/color/index.d.ts +12 -0
  50. package/dist/lib/color/index.d.ts.map +1 -0
  51. package/dist/lib/color/index.js +15 -0
  52. package/dist/lib/color/index.mjs +1 -0
  53. package/dist/lib/color/types.cjs +1 -0
  54. package/dist/lib/color/types.d.ts +179 -0
  55. package/dist/lib/color/types.d.ts.map +1 -0
  56. package/dist/lib/color/types.js +1 -0
  57. package/dist/lib/color/types.mjs +0 -0
  58. package/dist/lib/color/utils.cjs +1 -0
  59. package/dist/lib/color/utils.d.ts +199 -0
  60. package/dist/lib/color/utils.d.ts.map +1 -0
  61. package/dist/lib/color/utils.js +858 -0
  62. package/dist/lib/color/utils.mjs +1 -0
  63. package/dist/lib/compare/comparators.cjs +1 -0
  64. package/dist/lib/compare/comparators.d.ts +65 -0
  65. package/dist/lib/compare/comparators.d.ts.map +1 -0
  66. package/dist/lib/compare/comparators.js +210 -0
  67. package/dist/lib/compare/comparators.mjs +1 -0
  68. package/dist/lib/compare/index.cjs +1 -0
  69. package/dist/lib/compare/index.d.ts +2 -0
  70. package/dist/lib/compare/index.d.ts.map +1 -0
  71. package/dist/lib/compare/index.js +1 -0
  72. package/dist/lib/compare/index.mjs +1 -0
  73. package/dist/lib/crypto/hash.cjs +1 -0
  74. package/dist/lib/crypto/hash.d.ts +6 -0
  75. package/dist/lib/crypto/hash.d.ts.map +1 -0
  76. package/dist/lib/crypto/hash.js +53 -0
  77. package/dist/lib/crypto/hash.mjs +1 -0
  78. package/dist/lib/crypto/index.cjs +1 -0
  79. package/dist/lib/crypto/index.d.ts +2 -0
  80. package/dist/lib/crypto/index.d.ts.map +1 -0
  81. package/dist/lib/crypto/index.js +1 -0
  82. package/dist/lib/crypto/index.mjs +1 -0
  83. package/dist/lib/dom/events.cjs +1 -0
  84. package/dist/lib/dom/events.d.ts +159 -0
  85. package/dist/lib/dom/events.d.ts.map +1 -0
  86. package/dist/lib/dom/events.js +50 -0
  87. package/dist/lib/dom/events.mjs +1 -0
  88. package/dist/lib/dom/index.cjs +1 -0
  89. package/dist/lib/dom/index.d.ts +3 -0
  90. package/dist/lib/dom/index.d.ts.map +1 -0
  91. package/dist/lib/dom/index.js +2 -0
  92. package/dist/lib/dom/index.mjs +1 -0
  93. package/dist/lib/dom/utils.cjs +1 -0
  94. package/dist/lib/dom/utils.d.ts +6 -0
  95. package/dist/lib/dom/utils.d.ts.map +1 -0
  96. package/dist/lib/dom/utils.js +37 -0
  97. package/dist/lib/dom/utils.mjs +1 -0
  98. package/dist/lib/functions/functions.cjs +1 -0
  99. package/dist/lib/functions/functions.d.ts +329 -0
  100. package/dist/lib/functions/functions.d.ts.map +1 -0
  101. package/dist/lib/functions/functions.js +585 -0
  102. package/dist/lib/functions/functions.mjs +1 -0
  103. package/dist/lib/functions/index.cjs +1 -0
  104. package/dist/lib/functions/index.d.ts +3 -0
  105. package/dist/lib/functions/index.d.ts.map +1 -0
  106. package/dist/lib/functions/index.js +1 -0
  107. package/dist/lib/functions/index.mjs +1 -0
  108. package/dist/lib/functions/types.cjs +1 -0
  109. package/dist/lib/functions/types.d.ts +2 -0
  110. package/dist/lib/functions/types.d.ts.map +1 -0
  111. package/dist/lib/functions/types.js +1 -0
  112. package/dist/lib/functions/types.mjs +0 -0
  113. package/dist/lib/helpers.cjs +1 -0
  114. package/dist/lib/helpers.d.ts +13 -0
  115. package/dist/lib/helpers.d.ts.map +1 -0
  116. package/dist/lib/helpers.js +66 -0
  117. package/dist/lib/helpers.mjs +1 -0
  118. package/dist/lib/index.cjs +1 -0
  119. package/dist/lib/index.d.ts +6 -0
  120. package/dist/lib/index.d.ts.map +1 -0
  121. package/dist/lib/index.js +8 -0
  122. package/dist/lib/index.mjs +1 -0
  123. package/dist/lib/maths/constants.cjs +1 -0
  124. package/dist/lib/maths/constants.d.ts +84 -0
  125. package/dist/lib/maths/constants.d.ts.map +1 -0
  126. package/dist/lib/maths/constants.js +79 -0
  127. package/dist/lib/maths/constants.mjs +1 -0
  128. package/dist/lib/maths/fn/advanced.cjs +1 -0
  129. package/dist/lib/maths/fn/advanced.d.ts +149 -0
  130. package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
  131. package/dist/lib/maths/fn/advanced.js +264 -0
  132. package/dist/lib/maths/fn/advanced.mjs +1 -0
  133. package/dist/lib/maths/fn/basic.cjs +1 -0
  134. package/dist/lib/maths/fn/basic.d.ts +182 -0
  135. package/dist/lib/maths/fn/basic.d.ts.map +1 -0
  136. package/dist/lib/maths/fn/basic.js +343 -0
  137. package/dist/lib/maths/fn/basic.mjs +1 -0
  138. package/dist/lib/maths/fn/complex.cjs +1 -0
  139. package/dist/lib/maths/fn/complex.d.ts +153 -0
  140. package/dist/lib/maths/fn/complex.d.ts.map +1 -0
  141. package/dist/lib/maths/fn/complex.js +383 -0
  142. package/dist/lib/maths/fn/complex.mjs +1 -0
  143. package/dist/lib/maths/fn/index.cjs +1 -0
  144. package/dist/lib/maths/fn/index.d.ts +10 -0
  145. package/dist/lib/maths/fn/index.d.ts.map +1 -0
  146. package/dist/lib/maths/fn/index.js +9 -0
  147. package/dist/lib/maths/fn/index.mjs +1 -0
  148. package/dist/lib/maths/fn/trigo.cjs +1 -0
  149. package/dist/lib/maths/fn/trigo.d.ts +147 -0
  150. package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
  151. package/dist/lib/maths/fn/trigo.js +227 -0
  152. package/dist/lib/maths/fn/trigo.mjs +1 -0
  153. package/dist/lib/maths/fn/vector.cjs +1 -0
  154. package/dist/lib/maths/fn/vector.d.ts +512 -0
  155. package/dist/lib/maths/fn/vector.d.ts.map +1 -0
  156. package/dist/lib/maths/fn/vector.js +1136 -0
  157. package/dist/lib/maths/fn/vector.mjs +1 -0
  158. package/dist/lib/maths/formatter.cjs +1 -0
  159. package/dist/lib/maths/formatter.d.ts +35 -0
  160. package/dist/lib/maths/formatter.d.ts.map +1 -0
  161. package/dist/lib/maths/formatter.js +56 -0
  162. package/dist/lib/maths/formatter.mjs +1 -0
  163. package/dist/lib/maths/guards.cjs +1 -0
  164. package/dist/lib/maths/guards.d.ts +166 -0
  165. package/dist/lib/maths/guards.d.ts.map +1 -0
  166. package/dist/lib/maths/guards.js +315 -0
  167. package/dist/lib/maths/guards.mjs +1 -0
  168. package/dist/lib/maths/index.cjs +1 -0
  169. package/dist/lib/maths/index.d.ts +24 -0
  170. package/dist/lib/maths/index.d.ts.map +1 -0
  171. package/dist/lib/maths/index.js +29 -0
  172. package/dist/lib/maths/index.mjs +1 -0
  173. package/dist/lib/maths/parser.cjs +1 -0
  174. package/dist/lib/maths/parser.d.ts +20 -0
  175. package/dist/lib/maths/parser.d.ts.map +1 -0
  176. package/dist/lib/maths/parser.js +78 -0
  177. package/dist/lib/maths/parser.mjs +1 -0
  178. package/dist/lib/maths/random.cjs +1 -0
  179. package/dist/lib/maths/random.d.ts +27 -0
  180. package/dist/lib/maths/random.d.ts.map +1 -0
  181. package/dist/lib/maths/random.js +49 -0
  182. package/dist/lib/maths/random.mjs +1 -0
  183. package/dist/lib/maths/types/advanced.cjs +1 -0
  184. package/dist/lib/maths/types/advanced.d.ts +357 -0
  185. package/dist/lib/maths/types/advanced.d.ts.map +1 -0
  186. package/dist/lib/maths/types/advanced.js +5 -0
  187. package/dist/lib/maths/types/advanced.mjs +0 -0
  188. package/dist/lib/maths/types/basic.cjs +1 -0
  189. package/dist/lib/maths/types/basic.d.ts +95 -0
  190. package/dist/lib/maths/types/basic.d.ts.map +1 -0
  191. package/dist/lib/maths/types/basic.js +5 -0
  192. package/dist/lib/maths/types/basic.mjs +0 -0
  193. package/dist/lib/maths/types/index.cjs +1 -0
  194. package/dist/lib/maths/types/index.d.ts +4 -0
  195. package/dist/lib/maths/types/index.d.ts.map +1 -0
  196. package/dist/lib/maths/types/index.js +3 -0
  197. package/dist/lib/maths/types/index.mjs +0 -0
  198. package/dist/lib/maths/types/negative.cjs +1 -0
  199. package/dist/lib/maths/types/negative.d.ts +51 -0
  200. package/dist/lib/maths/types/negative.d.ts.map +1 -0
  201. package/dist/lib/maths/types/negative.js +5 -0
  202. package/dist/lib/maths/types/negative.mjs +0 -0
  203. package/dist/lib/maths/types/operations.cjs +1 -0
  204. package/dist/lib/maths/types/operations.d.ts +188 -0
  205. package/dist/lib/maths/types/operations.d.ts.map +1 -0
  206. package/dist/lib/maths/types/operations.js +6 -0
  207. package/dist/lib/maths/types/operations.mjs +0 -0
  208. package/dist/lib/object/clone.cjs +1 -0
  209. package/dist/lib/object/clone.d.ts +46 -0
  210. package/dist/lib/object/clone.d.ts.map +1 -0
  211. package/dist/lib/object/clone.js +104 -0
  212. package/dist/lib/object/clone.mjs +1 -0
  213. package/dist/lib/object/diff.cjs +1 -0
  214. package/dist/lib/object/diff.d.ts +203 -0
  215. package/dist/lib/object/diff.d.ts.map +1 -0
  216. package/dist/lib/object/diff.js +805 -0
  217. package/dist/lib/object/diff.mjs +1 -0
  218. package/dist/lib/object/getter-setter.cjs +1 -0
  219. package/dist/lib/object/getter-setter.d.ts +361 -0
  220. package/dist/lib/object/getter-setter.d.ts.map +1 -0
  221. package/dist/lib/object/getter-setter.js +1771 -0
  222. package/dist/lib/object/getter-setter.mjs +1 -0
  223. package/dist/lib/object/index.cjs +6 -0
  224. package/dist/lib/object/index.d.ts +13 -0
  225. package/dist/lib/object/index.d.ts.map +1 -0
  226. package/dist/lib/object/index.js +12 -0
  227. package/dist/lib/object/index.mjs +6 -0
  228. package/dist/lib/object/merge.cjs +1 -0
  229. package/dist/lib/object/merge.d.ts +93 -0
  230. package/dist/lib/object/merge.d.ts.map +1 -0
  231. package/dist/lib/object/merge.js +474 -0
  232. package/dist/lib/object/merge.mjs +1 -0
  233. package/dist/lib/object/object.cjs +1 -0
  234. package/dist/lib/object/object.d.ts +128 -0
  235. package/dist/lib/object/object.d.ts.map +1 -0
  236. package/dist/lib/object/object.js +432 -0
  237. package/dist/lib/object/object.mjs +1 -0
  238. package/dist/lib/object/omit.cjs +1 -0
  239. package/dist/lib/object/omit.d.ts +351 -0
  240. package/dist/lib/object/omit.d.ts.map +1 -0
  241. package/dist/lib/object/omit.js +656 -0
  242. package/dist/lib/object/omit.mjs +1 -0
  243. package/dist/lib/object/reconcile.cjs +1 -0
  244. package/dist/lib/object/reconcile.d.ts +7 -0
  245. package/dist/lib/object/reconcile.d.ts.map +1 -0
  246. package/dist/lib/object/reconcile.js +122 -0
  247. package/dist/lib/object/reconcile.mjs +1 -0
  248. package/dist/lib/object/selector.cjs +1 -0
  249. package/dist/lib/object/selector.d.ts +50 -0
  250. package/dist/lib/object/selector.d.ts.map +1 -0
  251. package/dist/lib/object/selector.js +371 -0
  252. package/dist/lib/object/selector.mjs +1 -0
  253. package/dist/lib/object/sort.cjs +1 -0
  254. package/dist/lib/object/sort.d.ts +149 -0
  255. package/dist/lib/object/sort.d.ts.map +1 -0
  256. package/dist/lib/object/sort.js +298 -0
  257. package/dist/lib/object/sort.mjs +1 -0
  258. package/dist/lib/object/transform.cjs +1 -0
  259. package/dist/lib/object/transform.d.ts +620 -0
  260. package/dist/lib/object/transform.d.ts.map +1 -0
  261. package/dist/lib/object/transform.js +1058 -0
  262. package/dist/lib/object/transform.mjs +1 -0
  263. package/dist/lib/object/types.cjs +1 -0
  264. package/dist/lib/object/types.d.ts +250 -0
  265. package/dist/lib/object/types.d.ts.map +1 -0
  266. package/dist/lib/object/types.js +1 -0
  267. package/dist/lib/object/types.mjs +0 -0
  268. package/dist/lib/object/utils.cjs +6 -0
  269. package/dist/lib/object/utils.d.ts +165 -0
  270. package/dist/lib/object/utils.d.ts.map +1 -0
  271. package/dist/lib/object/utils.js +444 -0
  272. package/dist/lib/object/utils.mjs +6 -0
  273. package/dist/lib/primitive/boolean/boolean.cjs +1 -0
  274. package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
  275. package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
  276. package/dist/lib/primitive/boolean/boolean.js +615 -0
  277. package/dist/lib/primitive/boolean/boolean.mjs +1 -0
  278. package/dist/lib/primitive/boolean/index.cjs +1 -0
  279. package/dist/lib/primitive/boolean/index.d.ts +3 -0
  280. package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
  281. package/dist/lib/primitive/boolean/index.js +2 -0
  282. package/dist/lib/primitive/boolean/index.mjs +1 -0
  283. package/dist/lib/primitive/boolean/types.cjs +1 -0
  284. package/dist/lib/primitive/boolean/types.d.ts +14 -0
  285. package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
  286. package/dist/lib/primitive/boolean/types.js +1 -0
  287. package/dist/lib/primitive/boolean/types.mjs +0 -0
  288. package/dist/lib/primitive/date/date.cjs +1 -0
  289. package/dist/lib/primitive/date/date.d.ts +641 -0
  290. package/dist/lib/primitive/date/date.d.ts.map +1 -0
  291. package/dist/lib/primitive/date/date.js +1998 -0
  292. package/dist/lib/primitive/date/date.mjs +1 -0
  293. package/dist/lib/primitive/date/index.cjs +1 -0
  294. package/dist/lib/primitive/date/index.d.ts +3 -0
  295. package/dist/lib/primitive/date/index.d.ts.map +1 -0
  296. package/dist/lib/primitive/date/index.js +2 -0
  297. package/dist/lib/primitive/date/index.mjs +1 -0
  298. package/dist/lib/primitive/date/types.cjs +1 -0
  299. package/dist/lib/primitive/date/types.d.ts +205 -0
  300. package/dist/lib/primitive/date/types.d.ts.map +1 -0
  301. package/dist/lib/primitive/date/types.js +7 -0
  302. package/dist/lib/primitive/date/types.mjs +0 -0
  303. package/dist/lib/primitive/index.cjs +5 -0
  304. package/dist/lib/primitive/index.d.ts +5 -0
  305. package/dist/lib/primitive/index.d.ts.map +1 -0
  306. package/dist/lib/primitive/index.js +4 -0
  307. package/dist/lib/primitive/index.mjs +5 -0
  308. package/dist/lib/primitive/string/index.cjs +5 -0
  309. package/dist/lib/primitive/string/index.d.ts +3 -0
  310. package/dist/lib/primitive/string/index.d.ts.map +1 -0
  311. package/dist/lib/primitive/string/index.js +2 -0
  312. package/dist/lib/primitive/string/index.mjs +5 -0
  313. package/dist/lib/primitive/string/parser-2.cjs +8 -0
  314. package/dist/lib/primitive/string/parser-2.d.ts +17 -0
  315. package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
  316. package/dist/lib/primitive/string/parser-2.js +656 -0
  317. package/dist/lib/primitive/string/parser-2.mjs +8 -0
  318. package/dist/lib/primitive/string/parser.cjs +3 -0
  319. package/dist/lib/primitive/string/parser.d.ts +9 -0
  320. package/dist/lib/primitive/string/parser.d.ts.map +1 -0
  321. package/dist/lib/primitive/string/parser.js +775 -0
  322. package/dist/lib/primitive/string/parser.mjs +3 -0
  323. package/dist/lib/primitive/string/string.cjs +5 -0
  324. package/dist/lib/primitive/string/string.d.ts +535 -0
  325. package/dist/lib/primitive/string/string.d.ts.map +1 -0
  326. package/dist/lib/primitive/string/string.js +1301 -0
  327. package/dist/lib/primitive/string/string.mjs +5 -0
  328. package/dist/lib/primitive/string/types.cjs +1 -0
  329. package/dist/lib/primitive/string/types.d.ts +318 -0
  330. package/dist/lib/primitive/string/types.d.ts.map +1 -0
  331. package/dist/lib/primitive/string/types.js +5 -0
  332. package/dist/lib/primitive/string/types.mjs +0 -0
  333. package/dist/lib/primitive/types.cjs +1 -0
  334. package/dist/lib/primitive/types.d.ts +27 -0
  335. package/dist/lib/primitive/types.d.ts.map +1 -0
  336. package/dist/lib/primitive/types.js +1 -0
  337. package/dist/lib/primitive/types.mjs +0 -0
  338. package/dist/lib/url/index.cjs +1 -0
  339. package/dist/lib/url/index.d.ts +3 -0
  340. package/dist/lib/url/index.d.ts.map +1 -0
  341. package/dist/lib/url/index.js +2 -0
  342. package/dist/lib/url/index.mjs +1 -0
  343. package/dist/lib/url/parser.cjs +1 -0
  344. package/dist/lib/url/parser.d.ts +216 -0
  345. package/dist/lib/url/parser.d.ts.map +1 -0
  346. package/dist/lib/url/parser.js +488 -0
  347. package/dist/lib/url/parser.mjs +1 -0
  348. package/dist/lib/url/types.cjs +1 -0
  349. package/dist/lib/url/types.d.ts +18 -0
  350. package/dist/lib/url/types.d.ts.map +1 -0
  351. package/dist/lib/url/types.js +1 -0
  352. package/dist/lib/url/types.mjs +0 -0
  353. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  354. package/package.json +150 -0
@@ -0,0 +1,2429 @@
1
+ /**
2
+ * @fileoverview Advanced array utilities with object integration
3
+ * Provides powerful array manipulation functions that work seamlessly with objects
4
+ */
5
+ import { isObject } from '../object/utils.js';
6
+ import { deepClone } from '../object/clone.js';
7
+ import { get } from '../object/getter-setter.js';
8
+ /**
9
+ * Chunks an array into smaller arrays of specified size
10
+ * @param array - Source array
11
+ * @param size - Chunk size
12
+ * @returns Array of chunks
13
+ * @example
14
+ * chunk([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
15
+ */
16
+ export function chunk(array, size) {
17
+ if (!Array.isArray(array) || size < 1) {
18
+ return [];
19
+ }
20
+ const chunks = [];
21
+ for (let i = 0; i < array.length; i += size) {
22
+ chunks.push(array.slice(i, i + size));
23
+ }
24
+ return chunks;
25
+ }
26
+ /**
27
+ * Flattens array by one level
28
+ * @param array - Source array
29
+ * @returns Flattened array
30
+ * @example
31
+ * flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
32
+ */
33
+ export function flatten(array) {
34
+ return array.reduce((acc, val) => {
35
+ return acc.concat(val);
36
+ }, []);
37
+ }
38
+ /**
39
+ * Deeply flattens nested arrays
40
+ * @param array - Source array
41
+ * @param depth - Maximum depth (default: Infinity)
42
+ * @returns Flattened array
43
+ * @example
44
+ * flattenDeep([[1, [2, [3, [4]]]]) // [1, 2, 3, 4]
45
+ */
46
+ export function flattenDeep(array, depth = Infinity) {
47
+ if (depth < 1) {
48
+ return array.slice();
49
+ }
50
+ return array.reduce((acc, val) => {
51
+ if (Array.isArray(val)) {
52
+ acc.push(...flattenDeep(val, depth - 1));
53
+ }
54
+ else {
55
+ acc.push(val);
56
+ }
57
+ return acc;
58
+ }, []);
59
+ }
60
+ /**
61
+ * Returns unique values from array
62
+ * @param array - Source array
63
+ * @param selector - Optional key path or function for comparison
64
+ * @returns Array with unique values
65
+ * @example
66
+ * unique([1, 2, 2, 3]) // [1, 2, 3]
67
+ * unique([{id: 1}, {id: 2}, {id: 1}], 'id') // [{id: 1}, {id: 2}]
68
+ * unique([{x: 1}, {x: 2}, {x: 1}], item => item.x) // [{x: 1}, {x: 2}]
69
+ */
70
+ export function unique(array, selector) {
71
+ if (!Array.isArray(array)) {
72
+ return [];
73
+ }
74
+ if (!selector) {
75
+ return [...new Set(array)];
76
+ }
77
+ const seen = new Set();
78
+ return array.filter((item) => {
79
+ const value = typeof selector === 'function'
80
+ ? selector(item)
81
+ : isObject(item)
82
+ ? get(item, selector)
83
+ : item;
84
+ if (seen.has(value)) {
85
+ return false;
86
+ }
87
+ seen.add(value);
88
+ return true;
89
+ });
90
+ }
91
+ /**
92
+ * Groups array elements by key or callback
93
+ * @param array - Source array
94
+ * @param keyOrFn - Key name or grouping function
95
+ * @returns Grouped object
96
+ * @example
97
+ * groupBy([{type: 'a', v: 1}, {type: 'b', v: 2}, {type: 'a', v: 3}], 'type')
98
+ * // {a: [{type: 'a', v: 1}, {type: 'a', v: 3}], b: [{type: 'b', v: 2}]}
99
+ */
100
+ export function groupBy(array, keyOrFn) {
101
+ if (!Array.isArray(array)) {
102
+ return {};
103
+ }
104
+ return array.reduce((acc, item) => {
105
+ let key;
106
+ if (typeof keyOrFn === 'function') {
107
+ key = String(keyOrFn(item));
108
+ }
109
+ else {
110
+ key = String(isObject(item) ? get(item, keyOrFn) : item);
111
+ }
112
+ if (!acc[key]) {
113
+ acc[key] = [];
114
+ }
115
+ acc[key].push(item);
116
+ return acc;
117
+ }, {});
118
+ }
119
+ /**
120
+ * Counts occurrences of each value
121
+ * @param array - Source array
122
+ * @param key - Optional key for object arrays
123
+ * @returns Count object
124
+ * @example
125
+ * countBy(['a', 'b', 'a', 'c']) // {a: 2, b: 1, c: 1}
126
+ */
127
+ export function countBy(array, key) {
128
+ if (!Array.isArray(array)) {
129
+ return {};
130
+ }
131
+ return array.reduce((acc, item) => {
132
+ let countKey;
133
+ if (typeof key === 'function') {
134
+ countKey = String(key(item));
135
+ }
136
+ else if (key) {
137
+ countKey = String(isObject(item) ? get(item, key) : item);
138
+ }
139
+ else {
140
+ countKey = String(item);
141
+ }
142
+ acc[countKey] = (acc[countKey] || 0) + 1;
143
+ return acc;
144
+ }, {});
145
+ }
146
+ /**
147
+ * Creates object from array using key/value extractors
148
+ * @param array - Source array
149
+ * @param keyFn - Key extractor function or key name
150
+ * @param valueFn - Value extractor function or value name (optional)
151
+ * @returns Object
152
+ * @example
153
+ * keyBy([{id: 1, name: 'a'}, {id: 2, name: 'b'}], 'id')
154
+ * // {1: {id: 1, name: 'a'}, 2: {id: 2, name: 'b'}}
155
+ */
156
+ export function keyBy(array, keyFn, valueFn) {
157
+ if (!Array.isArray(array)) {
158
+ return {};
159
+ }
160
+ return array.reduce((acc, item) => {
161
+ let key;
162
+ if (typeof keyFn === 'function') {
163
+ key = String(keyFn(item));
164
+ }
165
+ else {
166
+ key = String(isObject(item) ? get(item, keyFn) : item);
167
+ }
168
+ let value;
169
+ if (valueFn) {
170
+ if (typeof valueFn === 'function') {
171
+ value = valueFn(item);
172
+ }
173
+ else {
174
+ value = get(item, valueFn);
175
+ }
176
+ }
177
+ else {
178
+ value = item;
179
+ }
180
+ acc[key] = value;
181
+ return acc;
182
+ }, {});
183
+ }
184
+ /**
185
+ * Sorts array by key or multiple keys
186
+ * @param array - Source array
187
+ * @param keys - Sort key(s) with optional direction
188
+ * @returns Sorted array (new array)
189
+ * @example
190
+ * sortBy([{a: 3}, {a: 1}, {a: 2}], 'a') // [{a: 1}, {a: 2}, {a: 3}]
191
+ * sortBy([{a: 1, b: 3}, {a: 1, b: 1}], ['a', {key: 'b', dir: 'desc'}])
192
+ */
193
+ export function sortBy(array, keys) {
194
+ if (!Array.isArray(array) || array.length === 0) {
195
+ return [];
196
+ }
197
+ const sorted = [...array];
198
+ const keysArray = Array.isArray(keys) ? keys : [keys];
199
+ const normalizedKeys = keysArray.map((k) => {
200
+ if (typeof k === 'object' && 'key' in k) {
201
+ return { key: k.key, dir: k.dir || 'asc' };
202
+ }
203
+ return { key: k, dir: 'asc' };
204
+ });
205
+ sorted.sort((a, b) => {
206
+ for (const { key, dir } of normalizedKeys) {
207
+ const aVal = isObject(a) ? get(a, key) : a;
208
+ const bVal = isObject(b) ? get(b, key) : b;
209
+ if (aVal === bVal)
210
+ continue;
211
+ const comparison = aVal < bVal ? -1 : 1;
212
+ return dir === 'asc' ? comparison : -comparison;
213
+ }
214
+ return 0;
215
+ });
216
+ return sorted;
217
+ }
218
+ /**
219
+ * Finds difference between two arrays
220
+ * @param array1 - First array
221
+ * @param array2 - Second array
222
+ * @param key - Optional key for object comparison
223
+ * @returns Elements in array1 not in array2
224
+ * @example
225
+ * difference([1, 2, 3], [2, 3, 4]) // [1]
226
+ */
227
+ export function difference(array1, array2, key) {
228
+ if (!Array.isArray(array1))
229
+ return [];
230
+ if (!Array.isArray(array2))
231
+ return array1;
232
+ if (!key) {
233
+ const set2 = new Set(array2);
234
+ return array1.filter((item) => !set2.has(item));
235
+ }
236
+ const values2 = new Set(array2.map((item) => isObject(item) ? get(item, key) : item));
237
+ return array1.filter((item) => {
238
+ const value = isObject(item) ? get(item, key) : item;
239
+ return !values2.has(value);
240
+ });
241
+ }
242
+ /**
243
+ * Finds intersection of two arrays
244
+ * @param array1 - First array
245
+ * @param array2 - Second array
246
+ * @param key - Optional key for object comparison
247
+ * @returns Common elements
248
+ * @example
249
+ * intersection([1, 2, 3], [2, 3, 4]) // [2, 3]
250
+ */
251
+ export function intersection(array1, array2, key) {
252
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
253
+ return [];
254
+ }
255
+ if (!key) {
256
+ const set2 = new Set(array2);
257
+ return array1.filter((item) => set2.has(item));
258
+ }
259
+ const values2 = new Set(array2.map((item) => isObject(item) ? get(item, key) : item));
260
+ return array1.filter((item) => {
261
+ const value = isObject(item) ? get(item, key) : item;
262
+ return values2.has(value);
263
+ });
264
+ }
265
+ /**
266
+ * Finds union of two arrays (unique elements from both)
267
+ * @param array1 - First array
268
+ * @param array2 - Second array
269
+ * @param key - Optional key for object comparison
270
+ * @returns Union of arrays
271
+ * @example
272
+ * union([1, 2], [2, 3]) // [1, 2, 3]
273
+ */
274
+ export function union(array1, array2, key) {
275
+ if (!Array.isArray(array1) && !Array.isArray(array2)) {
276
+ return [];
277
+ }
278
+ if (!Array.isArray(array1))
279
+ return unique(array2, key);
280
+ if (!Array.isArray(array2))
281
+ return unique(array1, key);
282
+ return unique([...array1, ...array2], key);
283
+ }
284
+ /**
285
+ * Partitions array into two groups based on predicate
286
+ * @param array - Source array
287
+ * @param predicate - Test function
288
+ * @returns [truthy, falsy]
289
+ * @example
290
+ * partition([1, 2, 3, 4], x => x % 2 === 0) // [[2, 4], [1, 3]]
291
+ */
292
+ export function partition(array, predicate) {
293
+ if (!Array.isArray(array)) {
294
+ return [[], []];
295
+ }
296
+ return array.reduce((acc, item, index) => {
297
+ acc[predicate(item, index) ? 0 : 1].push(item);
298
+ return acc;
299
+ }, [[], []]);
300
+ }
301
+ /**
302
+ * Takes first n elements from array
303
+ * @param array - Source array
304
+ * @param n - Number of elements
305
+ * @returns First n elements
306
+ * @example
307
+ * take([1, 2, 3, 4], 2) // [1, 2]
308
+ */
309
+ export function take(array, n) {
310
+ if (!Array.isArray(array) || n <= 0) {
311
+ return [];
312
+ }
313
+ return array.slice(0, n);
314
+ }
315
+ /**
316
+ * Takes last n elements from array
317
+ * @param array - Source array
318
+ * @param n - Number of elements
319
+ * @returns Last n elements
320
+ * @example
321
+ * takeLast([1, 2, 3, 4], 2) // [3, 4]
322
+ */
323
+ export function takeLast(array, n) {
324
+ if (!Array.isArray(array) || n <= 0) {
325
+ return [];
326
+ }
327
+ return array.slice(-n);
328
+ }
329
+ /**
330
+ * Takes elements while predicate is true
331
+ * @param array - Source array
332
+ * @param predicate - Test function
333
+ * @returns Elements taken
334
+ * @example
335
+ * takeWhile([1, 2, 3, 4], x => x < 3) // [1, 2]
336
+ */
337
+ export function takeWhile(array, predicate) {
338
+ if (!Array.isArray(array)) {
339
+ return [];
340
+ }
341
+ const result = [];
342
+ for (let i = 0; i < array.length; i++) {
343
+ if (!predicate(array[i], i))
344
+ break;
345
+ result.push(array[i]);
346
+ }
347
+ return result;
348
+ }
349
+ /**
350
+ * Drops first n elements from array
351
+ * @param array - Source array
352
+ * @param n - Number of elements to drop
353
+ * @returns Remaining elements
354
+ * @example
355
+ * drop([1, 2, 3, 4], 2) // [3, 4]
356
+ */
357
+ export function drop(array, n) {
358
+ if (!Array.isArray(array) || n <= 0) {
359
+ return array ? [...array] : [];
360
+ }
361
+ return array.slice(n);
362
+ }
363
+ /**
364
+ * Drops elements while predicate is true
365
+ * @param array - Source array
366
+ * @param predicate - Test function
367
+ * @returns Remaining elements
368
+ * @example
369
+ * dropWhile([1, 2, 3, 4], x => x < 3) // [3, 4]
370
+ */
371
+ export function dropWhile(array, predicate) {
372
+ if (!Array.isArray(array)) {
373
+ return [];
374
+ }
375
+ let index = 0;
376
+ while (index < array.length && predicate(array[index], index)) {
377
+ index++;
378
+ }
379
+ return array.slice(index);
380
+ }
381
+ /**
382
+ * Compacts array by removing falsy values
383
+ * @param array - Source array
384
+ * @returns Array without falsy values
385
+ * @example
386
+ * compact([0, 1, false, 2, '', 3, null]) // [1, 2, 3]
387
+ */
388
+ export function compact(array) {
389
+ if (!Array.isArray(array)) {
390
+ return [];
391
+ }
392
+ return array.filter(Boolean);
393
+ }
394
+ /**
395
+ * Zips multiple arrays together
396
+ * @param arrays - Arrays to zip
397
+ * @returns Zipped array
398
+ * @example
399
+ * zip([1, 2], ['a', 'b'], [true, false]) // [[1, 'a', true], [2, 'b', false]]
400
+ */
401
+ export function zip(...arrays) {
402
+ if (arrays.length === 0)
403
+ return [];
404
+ const maxLength = Math.max(...arrays.map((arr) => (Array.isArray(arr) ? arr.length : 0)));
405
+ const result = [];
406
+ for (let i = 0; i < maxLength; i++) {
407
+ const tuple = [];
408
+ for (const array of arrays) {
409
+ tuple.push(Array.isArray(array) ? array[i] : undefined);
410
+ }
411
+ result.push(tuple);
412
+ }
413
+ return result;
414
+ }
415
+ /**
416
+ * Unzips array of arrays
417
+ * @param array - Array to unzip
418
+ * @returns Unzipped arrays
419
+ * @example
420
+ * unzip([[1, 'a'], [2, 'b']]) // [[1, 2], ['a', 'b']]
421
+ */
422
+ export function unzip(array) {
423
+ if (!Array.isArray(array) || array.length === 0) {
424
+ return [];
425
+ }
426
+ const maxLength = Math.max(...array.map((arr) => (Array.isArray(arr) ? arr.length : 0)));
427
+ const result = [];
428
+ for (let i = 0; i < maxLength; i++) {
429
+ const tuple = [];
430
+ for (const arr of array) {
431
+ if (Array.isArray(arr)) {
432
+ tuple.push(arr[i]);
433
+ }
434
+ }
435
+ result.push(tuple);
436
+ }
437
+ return result;
438
+ }
439
+ /**
440
+ * Shuffles array randomly
441
+ * @param array - Source array
442
+ * @returns Shuffled array (new array)
443
+ * @example
444
+ * shuffle([1, 2, 3, 4, 5])
445
+ */
446
+ export function shuffle(array) {
447
+ if (!Array.isArray(array)) {
448
+ return [];
449
+ }
450
+ const result = [...array];
451
+ for (let i = result.length - 1; i > 0; i--) {
452
+ const j = Math.floor(Math.random() * (i + 1));
453
+ [result[i], result[j]] = [result[j], result[i]];
454
+ }
455
+ return result;
456
+ }
457
+ /**
458
+ * Gets random element from array
459
+ * @param array - Source array
460
+ * @returns Random element
461
+ * @example
462
+ * sample([1, 2, 3, 4, 5]) // Random element
463
+ */
464
+ export function sample(array) {
465
+ if (!Array.isArray(array) || array.length === 0) {
466
+ return undefined;
467
+ }
468
+ return array[Math.floor(Math.random() * array.length)];
469
+ }
470
+ /**
471
+ * Gets n random elements from array
472
+ * @param array - Source array
473
+ * @param n - Number of elements
474
+ * @returns Random elements
475
+ * @example
476
+ * sampleSize([1, 2, 3, 4, 5], 3) // 3 random elements
477
+ */
478
+ export function sampleSize(array, n) {
479
+ if (!Array.isArray(array) || n <= 0) {
480
+ return [];
481
+ }
482
+ const shuffled = shuffle(array);
483
+ return shuffled.slice(0, Math.min(n, array.length));
484
+ }
485
+ /**
486
+ * Finds index of element using predicate
487
+ * @param array - Source array
488
+ * @param predicate - Search function
489
+ * @param fromIndex - Start index
490
+ * @returns Index or -1
491
+ * @example
492
+ * findIndex([1, 2, 3, 4], x => x > 2) // 2
493
+ */
494
+ export function findIndex(array, predicate, fromIndex = 0) {
495
+ if (!Array.isArray(array)) {
496
+ return -1;
497
+ }
498
+ for (let i = fromIndex; i < array.length; i++) {
499
+ if (predicate(array[i], i)) {
500
+ return i;
501
+ }
502
+ }
503
+ return -1;
504
+ }
505
+ /**
506
+ * Finds last index of element using predicate
507
+ * @param array - Source array
508
+ * @param predicate - Search function
509
+ * @param fromIndex - Start index (from end)
510
+ * @returns Index or -1
511
+ * @example
512
+ * findLastIndex([1, 2, 3, 4, 3], x => x === 3) // 4
513
+ */
514
+ export function findLastIndex(array, predicate, fromIndex) {
515
+ if (!Array.isArray(array)) {
516
+ return -1;
517
+ }
518
+ const startIndex = fromIndex !== undefined ? fromIndex : array.length - 1;
519
+ for (let i = startIndex; i >= 0; i--) {
520
+ if (predicate(array[i], i)) {
521
+ return i;
522
+ }
523
+ }
524
+ return -1;
525
+ }
526
+ /**
527
+ * Creates array of values at given paths
528
+ * @param array - Source array of objects
529
+ * @param path - Property path
530
+ * @returns Array of values
531
+ * @example
532
+ * pluck([{a: {b: 1}}, {a: {b: 2}}], 'a.b') // [1, 2]
533
+ */
534
+ export function pluck(array, path) {
535
+ if (!Array.isArray(array)) {
536
+ return [];
537
+ }
538
+ return array.map((item) => get(item, path));
539
+ }
540
+ /**
541
+ * Extracts object properties from array
542
+ * @param array - Source array of objects
543
+ * @param keys - Keys to pick
544
+ * @returns Array of objects with picked keys
545
+ * @example
546
+ * pick([{a: 1, b: 2}, {a: 3, b: 4}], ['a']) // [{a: 1}, {a: 3}]
547
+ */
548
+ export function pick(array, keys) {
549
+ if (!Array.isArray(array) || !Array.isArray(keys)) {
550
+ return [];
551
+ }
552
+ return array.map((item) => {
553
+ const result = {};
554
+ for (const key of keys) {
555
+ if (key in item) {
556
+ result[key] = item[key];
557
+ }
558
+ }
559
+ return result;
560
+ });
561
+ }
562
+ /**
563
+ * Omits object properties from array
564
+ * @param array - Source array of objects
565
+ * @param keys - Keys to omit
566
+ * @returns Array of objects without omitted keys
567
+ * @example
568
+ * omit([{a: 1, b: 2}, {a: 3, b: 4}], ['b']) // [{a: 1}, {a: 3}]
569
+ */
570
+ export function omit(array, keys) {
571
+ if (!Array.isArray(array) || !Array.isArray(keys)) {
572
+ return [];
573
+ }
574
+ return array.map((item) => {
575
+ const result = { ...item };
576
+ for (const key of keys) {
577
+ delete result[key];
578
+ }
579
+ return result;
580
+ });
581
+ }
582
+ /**
583
+ * Maps and flattens array in one operation
584
+ * @param array - Source array
585
+ * @param callback - Mapping function
586
+ * @returns Flattened mapped array
587
+ * @example
588
+ * flatMap([1, 2, 3], x => [x, x * 2]) // [1, 2, 2, 4, 3, 6]
589
+ */
590
+ export function flatMap(array, callback) {
591
+ if (!Array.isArray(array)) {
592
+ return [];
593
+ }
594
+ return array.reduce((acc, item, index) => {
595
+ const result = callback(item, index);
596
+ return acc.concat(result);
597
+ }, []);
598
+ }
599
+ /**
600
+ * Reduces array from right to left
601
+ * @param array - Source array
602
+ * @param callback - Reducer function
603
+ * @param initialValue - Initial value
604
+ * @returns Reduced value
605
+ * @example
606
+ * reduceRight([1, 2, 3], (acc, val) => acc + val, 0) // 6
607
+ */
608
+ export function reduceRight(array, callback, initialValue) {
609
+ if (!Array.isArray(array)) {
610
+ return initialValue;
611
+ }
612
+ let accumulator = initialValue;
613
+ for (let i = array.length - 1; i >= 0; i--) {
614
+ accumulator = callback(accumulator, array[i], i);
615
+ }
616
+ return accumulator;
617
+ }
618
+ /**
619
+ * Creates array of numbers in range
620
+ * @param start - Start value
621
+ * @param end - End value (exclusive)
622
+ * @param step - Step value
623
+ * @returns Range array
624
+ * @example
625
+ * range(0, 5) // [0, 1, 2, 3, 4]
626
+ * range(0, 10, 2) // [0, 2, 4, 6, 8]
627
+ */
628
+ export function range(start, end, step = 1) {
629
+ if (step === 0) {
630
+ return [];
631
+ }
632
+ const result = [];
633
+ if (step > 0) {
634
+ for (let i = start; i < end; i += step) {
635
+ result.push(i);
636
+ }
637
+ }
638
+ else {
639
+ for (let i = start; i > end; i += step) {
640
+ result.push(i);
641
+ }
642
+ }
643
+ return result;
644
+ }
645
+ /**
646
+ * Fills array with value (similar to Array.prototype.fill)
647
+ * @param array - Source array
648
+ * @param value - Fill value
649
+ * @param start - Start index (default: 0)
650
+ * @param end - End index (default: array.length)
651
+ * @returns New array with filled values
652
+ * @example
653
+ * fill([1, 2, 3], 0) // [0, 0, 0]
654
+ * fill([1, 2, 3, 4, 5], 9, 1, 3) // [1, 9, 9, 4, 5]
655
+ */
656
+ export function fill(array, value, start = 0, end) {
657
+ if (!Array.isArray(array)) {
658
+ return [];
659
+ }
660
+ const result = [...array];
661
+ const actualEnd = end === undefined ? result.length : end;
662
+ for (let i = start; i < actualEnd && i < result.length; i++) {
663
+ result[i] = value;
664
+ }
665
+ return result;
666
+ }
667
+ /**
668
+ * Deeply clones array (including nested objects)
669
+ * @param array - Source array
670
+ * @returns Cloned array
671
+ * @example
672
+ * deepCloneArray([{a: 1}, {b: 2}])
673
+ */
674
+ export function deepCloneArray(array) {
675
+ if (!Array.isArray(array)) {
676
+ return [];
677
+ }
678
+ return deepClone(array);
679
+ }
680
+ /**
681
+ * Merges multiple arrays of objects by key
682
+ * @param arr1 - First array
683
+ * @param arr2 - Second array (or key if only merging arr1)
684
+ * @param keyOrMore - Key to merge by, or more arrays
685
+ * @returns Merged array
686
+ * @example
687
+ * mergeByKey([{id: 1, a: 1}], [{id: 1, b: 2}], 'id')
688
+ * // [{id: 1, a: 1, b: 2}]
689
+ */
690
+ export function mergeByKey(arr1, arr2, ...keyOrMore) {
691
+ // Determine the key and arrays
692
+ let key;
693
+ let arrays;
694
+ if (typeof arr2 === 'string') {
695
+ // Form: mergeByKey([arr], 'key')
696
+ key = arr2;
697
+ arrays = [arr1];
698
+ }
699
+ else {
700
+ // Form: mergeByKey(arr1, arr2, 'key') or mergeByKey(arr1, arr2, arr3, 'key')
701
+ const lastArg = keyOrMore[keyOrMore.length - 1];
702
+ if (typeof lastArg === 'string') {
703
+ key = lastArg;
704
+ arrays = [
705
+ arr1,
706
+ arr2,
707
+ ...keyOrMore.slice(0, -1).filter(Array.isArray),
708
+ ];
709
+ }
710
+ else {
711
+ // No key provided, default to 'id'
712
+ key = 'id';
713
+ arrays = [arr1, arr2, ...keyOrMore.filter(Array.isArray)];
714
+ }
715
+ }
716
+ if (arrays.length === 0) {
717
+ return [];
718
+ }
719
+ const map = new Map();
720
+ for (const array of arrays) {
721
+ if (Array.isArray(array)) {
722
+ for (const item of array) {
723
+ const keyValue = isObject(item)
724
+ ? get(item, key)
725
+ : undefined;
726
+ if (keyValue !== undefined) {
727
+ const existing = map.get(keyValue);
728
+ map.set(keyValue, existing ? { ...existing, ...item } : item);
729
+ }
730
+ }
731
+ }
732
+ }
733
+ return Array.from(map.values());
734
+ }
735
+ /**
736
+ * Moves array element from one index to another
737
+ * @param array - Source array
738
+ * @param fromIndex - Source index
739
+ * @param toIndex - Destination index
740
+ * @returns New array with moved element
741
+ * @example
742
+ * move([1, 2, 3, 4], 0, 2) // [2, 3, 1, 4]
743
+ */
744
+ export function move(array, fromIndex, toIndex) {
745
+ if (!Array.isArray(array) || fromIndex === toIndex) {
746
+ return array ? [...array] : [];
747
+ }
748
+ const result = [...array];
749
+ const [removed] = result.splice(fromIndex, 1);
750
+ result.splice(toIndex, 0, removed);
751
+ return result;
752
+ }
753
+ /**
754
+ * Rotates array elements
755
+ * @param array - Source array
756
+ * @param n - Number of positions (positive = right, negative = left)
757
+ * @returns Rotated array
758
+ * @example
759
+ * rotate([1, 2, 3, 4], 1) // [4, 1, 2, 3]
760
+ * rotate([1, 2, 3, 4], -1) // [2, 3, 4, 1]
761
+ */
762
+ export function rotate(array, n) {
763
+ if (!Array.isArray(array) || array.length === 0 || n === 0) {
764
+ return array ? [...array] : [];
765
+ }
766
+ const len = array.length;
767
+ const rotations = ((n % len) + len) % len;
768
+ return [...array.slice(-rotations), ...array.slice(0, -rotations)];
769
+ }
770
+ /**
771
+ * Inserts element at index
772
+ * @param array - Source array
773
+ * @param index - Insert index
774
+ * @param items - Items to insert
775
+ * @returns New array
776
+ * @example
777
+ * insert([1, 2, 4], 2, 3) // [1, 2, 3, 4]
778
+ */
779
+ export function insert(array, index, ...items) {
780
+ if (!Array.isArray(array)) {
781
+ return items;
782
+ }
783
+ const result = [...array];
784
+ result.splice(index, 0, ...items);
785
+ return result;
786
+ }
787
+ /**
788
+ * Removes element at index
789
+ * @param array - Source array
790
+ * @param index - Remove index
791
+ * @param count - Number of elements to remove
792
+ * @returns New array
793
+ * @example
794
+ * remove([1, 2, 3, 4], 1, 2) // [1, 4]
795
+ */
796
+ export function remove(array, index, count = 1) {
797
+ if (!Array.isArray(array)) {
798
+ return [];
799
+ }
800
+ const result = [...array];
801
+ result.splice(index, count);
802
+ return result;
803
+ }
804
+ /**
805
+ * Replaces element at index
806
+ * @param array - Source array
807
+ * @param index - Replace index
808
+ * @param item - New item
809
+ * @returns New array
810
+ * @example
811
+ * replace([1, 2, 3], 1, 5) // [1, 5, 3]
812
+ */
813
+ export function replace(array, index, item) {
814
+ if (!Array.isArray(array)) {
815
+ return [];
816
+ }
817
+ const result = [...array];
818
+ result[index] = item;
819
+ return result;
820
+ }
821
+ /**
822
+ * Filters array and removes null/undefined
823
+ * @param array - Source array
824
+ * @param predicate - Filter function
825
+ * @returns Filtered array without null/undefined
826
+ * @example
827
+ * filterCompact([1, null, 2, undefined, 3], x => x) // [1, 2, 3]
828
+ */
829
+ export function filterCompact(array, predicate) {
830
+ if (!Array.isArray(array)) {
831
+ return [];
832
+ }
833
+ const filtered = array.filter((item) => item != null);
834
+ return predicate ? filtered.filter(predicate) : filtered;
835
+ }
836
+ /**
837
+ * Creates cartesian product of arrays
838
+ * @param arrays - Arrays to combine
839
+ * @returns Cartesian product
840
+ * @example
841
+ * cartesian([1, 2], ['a', 'b']) // [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]
842
+ */
843
+ export function cartesian(...arrays) {
844
+ if (arrays.length === 0)
845
+ return [[]];
846
+ if (arrays.length === 1)
847
+ return arrays[0].map((item) => [item]);
848
+ return arrays.reduce((acc, array) => {
849
+ return acc.flatMap((x) => array.map((y) => [...x, y]));
850
+ }, [[]]);
851
+ }
852
+ /**
853
+ * Finds all indices where predicate is true
854
+ * @param array - Source array
855
+ * @param predicate - Test function
856
+ * @returns Array of indices
857
+ * @example
858
+ * findAllIndices([1, 2, 3, 2, 1], x => x === 2) // [1, 3]
859
+ */
860
+ export function findAllIndices(array, predicate) {
861
+ if (!Array.isArray(array)) {
862
+ return [];
863
+ }
864
+ const indices = [];
865
+ for (let i = 0; i < array.length; i++) {
866
+ if (predicate(array[i], i)) {
867
+ indices.push(i);
868
+ }
869
+ }
870
+ return indices;
871
+ }
872
+ /**
873
+ * Checks if arrays are equal
874
+ * @param array1 - First array
875
+ * @param array2 - Second array
876
+ * @param deep - Deep comparison
877
+ * @returns True if equal
878
+ * @example
879
+ * isEqual([1, 2, 3], [1, 2, 3]) // true
880
+ */
881
+ export function isEqual(array1, array2, deep = true) {
882
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
883
+ return false;
884
+ }
885
+ if (array1.length !== array2.length) {
886
+ return false;
887
+ }
888
+ for (let i = 0; i < array1.length; i++) {
889
+ if (deep && isObject(array1[i]) && isObject(array2[i])) {
890
+ // Deep comparison using JSON (simple approach)
891
+ if (JSON.stringify(array1[i]) !== JSON.stringify(array2[i])) {
892
+ return false;
893
+ }
894
+ }
895
+ else if (array1[i] !== array2[i]) {
896
+ return false;
897
+ }
898
+ }
899
+ return true;
900
+ }
901
+ /**
902
+ * Splits array into chunks where predicate changes
903
+ * @param array - Source array
904
+ * @param predicate - Grouping function
905
+ * @returns Chunked array
906
+ * @example
907
+ * chunkBy([1, 1, 2, 2, 3], x => x) // [[1, 1], [2, 2], [3]]
908
+ */
909
+ export function chunkBy(array, predicate) {
910
+ if (!Array.isArray(array) || array.length === 0) {
911
+ return [];
912
+ }
913
+ const result = [];
914
+ let currentChunk = [array[0]];
915
+ let currentKey = predicate(array[0], 0);
916
+ for (let i = 1; i < array.length; i++) {
917
+ const key = predicate(array[i], i);
918
+ if (key === currentKey) {
919
+ currentChunk.push(array[i]);
920
+ }
921
+ else {
922
+ result.push(currentChunk);
923
+ currentChunk = [array[i]];
924
+ currentKey = key;
925
+ }
926
+ }
927
+ result.push(currentChunk);
928
+ return result;
929
+ }
930
+ /**
931
+ * Updates array elements by predicate or key/value
932
+ * @param array - Source array
933
+ * @param predicate - Predicate function or key to match
934
+ * @param updates - Updates to apply (or value if predicate is a key)
935
+ * @param updatesOrUndefined - Updates if using key/value form
936
+ * @returns Updated array
937
+ * @example
938
+ * updateBy([{id: 1, name: 'a'}], x => x.id === 1, {name: 'b'}) // [{id: 1, name: 'b'}]
939
+ * updateBy([{id: 1, name: 'a'}], 'id', 1, {name: 'b'}) // [{id: 1, name: 'b'}]
940
+ */
941
+ export function updateBy(array, predicate, updates, updatesOrUndefined) {
942
+ if (!Array.isArray(array)) {
943
+ return [];
944
+ }
945
+ // Determine if we're using predicate form or key/value form
946
+ const isPredicate = typeof predicate === 'function';
947
+ const actualUpdates = isPredicate ? updates : updatesOrUndefined;
948
+ return array.map((item) => {
949
+ let shouldUpdate = false;
950
+ if (isPredicate) {
951
+ shouldUpdate = predicate(item);
952
+ }
953
+ else {
954
+ const itemValue = isObject(item)
955
+ ? get(item, predicate)
956
+ : item;
957
+ shouldUpdate = itemValue === updates;
958
+ }
959
+ if (shouldUpdate) {
960
+ return { ...item, ...actualUpdates };
961
+ }
962
+ return item;
963
+ });
964
+ }
965
+ /**
966
+ * Removes duplicates and returns removed items
967
+ * @param array - Source array
968
+ * @param key - Optional key for objects
969
+ * @returns Object with unique and duplicates arrays
970
+ * @example
971
+ * splitDuplicates([1, 2, 2, 3, 3]) // { unique: [1, 2, 3], duplicates: [2, 3] }
972
+ */
973
+ export function splitDuplicates(array, key) {
974
+ if (!Array.isArray(array)) {
975
+ return { unique: [], duplicates: [] };
976
+ }
977
+ const seen = new Set();
978
+ const unique = [];
979
+ const duplicates = [];
980
+ for (const item of array) {
981
+ const value = key && isObject(item) ? get(item, key) : item;
982
+ if (seen.has(value)) {
983
+ duplicates.push(item);
984
+ }
985
+ else {
986
+ seen.add(value);
987
+ unique.push(item);
988
+ }
989
+ }
990
+ return { unique, duplicates };
991
+ }
992
+ /**
993
+ * Ensures array (converts non-array to array)
994
+ * @param value - Value to ensure as array
995
+ * @returns Array
996
+ * @example
997
+ * ensureArray(1) // [1]
998
+ * ensureArray([1, 2]) // [1, 2]
999
+ */
1000
+ export function ensureArray(value) {
1001
+ if (Array.isArray(value)) {
1002
+ return value;
1003
+ }
1004
+ return value === undefined || value === null ? [] : [value];
1005
+ }
1006
+ /**
1007
+ * Wraps value in array if not already an array
1008
+ * @param value - Value to wrap
1009
+ * @returns Array
1010
+ * @example
1011
+ * wrap(1) // [1]
1012
+ * wrap([1]) // [1]
1013
+ */
1014
+ export function wrap(value) {
1015
+ return ensureArray(value);
1016
+ }
1017
+ /**
1018
+ * Creates sliding windows of specified size over the array
1019
+ * @param array - Source array
1020
+ * @param size - Window size
1021
+ * @param step - Step size between windows (default: 1)
1022
+ * @returns Array of windows
1023
+ * @example
1024
+ * slidingWindow([1, 2, 3, 4, 5], 3) // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
1025
+ * slidingWindow([1, 2, 3, 4, 5], 2, 2) // [[1, 2], [3, 4]]
1026
+ */
1027
+ export function slidingWindow(array, size, step = 1) {
1028
+ if (!Array.isArray(array) || size < 1 || step < 1) {
1029
+ return [];
1030
+ }
1031
+ const windows = [];
1032
+ for (let i = 0; i <= array.length - size; i += step) {
1033
+ windows.push(array.slice(i, i + size));
1034
+ }
1035
+ return windows;
1036
+ }
1037
+ /**
1038
+ * Finds the minimum value in an array by comparing a key/selector
1039
+ * @param array - Source array
1040
+ * @param selector - Key path or selector function
1041
+ * @returns Element with minimum value, or undefined if array is empty
1042
+ * @example
1043
+ * minBy([{ age: 30 }, { age: 20 }], 'age') // { age: 20 }
1044
+ * minBy([{ price: 100 }, { price: 50 }], obj => obj.price) // { price: 50 }
1045
+ */
1046
+ export function minBy(array, selector) {
1047
+ if (!Array.isArray(array) || array.length === 0) {
1048
+ return undefined;
1049
+ }
1050
+ let minItem = array[0];
1051
+ let minValue = typeof selector === 'function'
1052
+ ? selector(minItem)
1053
+ : get(minItem, selector);
1054
+ for (let i = 1; i < array.length; i++) {
1055
+ const item = array[i];
1056
+ const value = typeof selector === 'function'
1057
+ ? selector(item)
1058
+ : get(item, selector);
1059
+ if (value < minValue) {
1060
+ minValue = value;
1061
+ minItem = item;
1062
+ }
1063
+ }
1064
+ return minItem;
1065
+ }
1066
+ /**
1067
+ * Finds the maximum value in an array by comparing a key/selector
1068
+ * @param array - Source array
1069
+ * @param selector - Key path or selector function
1070
+ * @returns Element with maximum value, or undefined if array is empty
1071
+ * @example
1072
+ * maxBy([{ age: 30 }, { age: 20 }], 'age') // { age: 30 }
1073
+ * maxBy([{ price: 100 }, { price: 50 }], obj => obj.price) // { price: 100 }
1074
+ */
1075
+ export function maxBy(array, selector) {
1076
+ if (!Array.isArray(array) || array.length === 0) {
1077
+ return undefined;
1078
+ }
1079
+ let maxItem = array[0];
1080
+ let maxValue = typeof selector === 'function'
1081
+ ? selector(maxItem)
1082
+ : get(maxItem, selector);
1083
+ for (let i = 1; i < array.length; i++) {
1084
+ const item = array[i];
1085
+ const value = typeof selector === 'function'
1086
+ ? selector(item)
1087
+ : get(item, selector);
1088
+ if (value > maxValue) {
1089
+ maxValue = value;
1090
+ maxItem = item;
1091
+ }
1092
+ }
1093
+ return maxItem;
1094
+ }
1095
+ /**
1096
+ * Calculates sum of values in array by key/selector
1097
+ * @param array - Source array
1098
+ * @param selector - Key path or selector function
1099
+ * @returns Sum of values
1100
+ * @example
1101
+ * sumBy([{ price: 10 }, { price: 20 }], 'price') // 30
1102
+ * sumBy([{ x: 5 }, { x: 10 }], obj => obj.x) // 15
1103
+ */
1104
+ export function sumBy(array, selector) {
1105
+ if (!Array.isArray(array) || array.length === 0) {
1106
+ return 0;
1107
+ }
1108
+ let sum = 0;
1109
+ for (const item of array) {
1110
+ const value = typeof selector === 'function'
1111
+ ? selector(item)
1112
+ : get(item, selector);
1113
+ if (typeof value === 'number' && !isNaN(value)) {
1114
+ sum += value;
1115
+ }
1116
+ }
1117
+ return sum;
1118
+ }
1119
+ /**
1120
+ * Calculates average of values in array by key/selector
1121
+ * @param array - Source array
1122
+ * @param selector - Key path or selector function
1123
+ * @returns Average of values, or 0 if array is empty
1124
+ * @example
1125
+ * avgBy([{ score: 80 }, { score: 90 }], 'score') // 85
1126
+ * avgBy([{ x: 10 }, { x: 20 }], obj => obj.x) // 15
1127
+ */
1128
+ export function avgBy(array, selector) {
1129
+ if (!Array.isArray(array) || array.length === 0) {
1130
+ return 0;
1131
+ }
1132
+ const sum = sumBy(array, selector);
1133
+ return sum / array.length;
1134
+ }
1135
+ /**
1136
+ * Counts frequency of each unique value in array
1137
+ * @param array - Source array
1138
+ * @returns Map of value to frequency count
1139
+ * @example
1140
+ * frequencies([1, 2, 2, 3, 3, 3]) // Map { 1 => 1, 2 => 2, 3 => 3 }
1141
+ * frequencies(['a', 'b', 'a']) // Map { 'a' => 2, 'b' => 1 }
1142
+ */
1143
+ export function frequencies(array) {
1144
+ const freqMap = new Map();
1145
+ if (!Array.isArray(array)) {
1146
+ return freqMap;
1147
+ }
1148
+ for (const item of array) {
1149
+ freqMap.set(item, (freqMap.get(item) || 0) + 1);
1150
+ }
1151
+ return freqMap;
1152
+ }
1153
+ /**
1154
+ * Finds the most common value(s) in array
1155
+ * @param array - Source array
1156
+ * @param limit - Maximum number of values to return (default: 1)
1157
+ * @returns Array of most common values
1158
+ * @example
1159
+ * mostCommon([1, 2, 2, 3, 3, 3]) // [3]
1160
+ * mostCommon([1, 1, 2, 2, 3], 2) // [1, 2] or [2, 1]
1161
+ */
1162
+ export function mostCommon(array, limit = 1) {
1163
+ if (!Array.isArray(array) || array.length === 0 || limit < 1) {
1164
+ return [];
1165
+ }
1166
+ const freqMap = frequencies(array);
1167
+ const sorted = Array.from(freqMap.entries()).sort((a, b) => b[1] - a[1]);
1168
+ return sorted.slice(0, limit).map(([value]) => value);
1169
+ }
1170
+ /**
1171
+ * Finds the least common value(s) in array
1172
+ * @param array - Source array
1173
+ * @param limit - Maximum number of values to return (default: 1)
1174
+ * @returns Array of least common values
1175
+ * @example
1176
+ * leastCommon([1, 2, 2, 3, 3, 3]) // [1]
1177
+ * leastCommon([1, 1, 2, 2, 3, 3], 3) // [1, 2, 3] or any order
1178
+ */
1179
+ export function leastCommon(array, limit = 1) {
1180
+ if (!Array.isArray(array) || array.length === 0 || limit < 1) {
1181
+ return [];
1182
+ }
1183
+ const freqMap = frequencies(array);
1184
+ const sorted = Array.from(freqMap.entries()).sort((a, b) => a[1] - b[1]);
1185
+ return sorted.slice(0, limit).map(([value]) => value);
1186
+ }
1187
+ /**
1188
+ * Processes array in batches with async callback
1189
+ * @param array - Source array
1190
+ * @param batchSize - Size of each batch
1191
+ * @param callback - Async function to process each batch
1192
+ * @returns Promise that resolves when all batches are processed
1193
+ * @example
1194
+ * await batchProcess([1, 2, 3, 4], 2, async (batch) => {
1195
+ * await saveToDatabase(batch);
1196
+ * });
1197
+ */
1198
+ export async function batchProcess(array, batchSize, callback) {
1199
+ if (!Array.isArray(array) || batchSize < 1) {
1200
+ return;
1201
+ }
1202
+ const batches = chunk(array, batchSize);
1203
+ for (let i = 0; i < batches.length; i++) {
1204
+ await callback(batches[i], i);
1205
+ }
1206
+ }
1207
+ /**
1208
+ * Processes array in batches in parallel with concurrency limit
1209
+ * @param array - Source array
1210
+ * @param batchSize - Size of each batch
1211
+ * @param concurrency - Maximum number of concurrent batches
1212
+ * @param callback - Async function to process each batch
1213
+ * @returns Promise that resolves when all batches are processed
1214
+ * @example
1215
+ * await batchProcessParallel([1, 2, 3, 4, 5, 6], 2, 2, async (batch) => {
1216
+ * await saveToDatabase(batch);
1217
+ * });
1218
+ */
1219
+ export async function batchProcessParallel(array, batchSize, concurrency, callback) {
1220
+ if (!Array.isArray(array) || batchSize < 1 || concurrency < 1) {
1221
+ return;
1222
+ }
1223
+ const batches = chunk(array, batchSize);
1224
+ const batchChunks = chunk(batches.map((batch, index) => ({ batch, index })), concurrency);
1225
+ for (const batchChunk of batchChunks) {
1226
+ await Promise.all(batchChunk.map(({ batch, index }) => callback(batch, index)));
1227
+ }
1228
+ }
1229
+ /**
1230
+ * Transposes a 2D array (rows become columns, columns become rows)
1231
+ * @param matrix - 2D array to transpose
1232
+ * @returns Transposed 2D array
1233
+ * @example
1234
+ * transpose([[1, 2], [3, 4], [5, 6]]) // [[1, 3, 5], [2, 4, 6]]
1235
+ */
1236
+ export function transpose(matrix) {
1237
+ if (!Array.isArray(matrix) || matrix.length === 0) {
1238
+ return [];
1239
+ }
1240
+ const maxLength = Math.max(...matrix.map((row) => row.length));
1241
+ const result = [];
1242
+ for (let col = 0; col < maxLength; col++) {
1243
+ const column = [];
1244
+ for (let row = 0; row < matrix.length; row++) {
1245
+ if (col < matrix[row].length) {
1246
+ column.push(matrix[row][col]);
1247
+ }
1248
+ }
1249
+ result.push(column);
1250
+ }
1251
+ return result;
1252
+ }
1253
+ /**
1254
+ * Checks if two arrays are equal (shallow comparison)
1255
+ * @param array1 - First array
1256
+ * @param array2 - Second array
1257
+ * @returns True if arrays are equal
1258
+ * @example
1259
+ * isArrayEqual([1, 2, 3], [1, 2, 3]) // true
1260
+ * isArrayEqual([1, 2], [1, 2, 3]) // false
1261
+ */
1262
+ export function isArrayEqual(array1, array2) {
1263
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
1264
+ return false;
1265
+ }
1266
+ if (array1.length !== array2.length) {
1267
+ return false;
1268
+ }
1269
+ for (let i = 0; i < array1.length; i++) {
1270
+ if (array1[i] !== array2[i]) {
1271
+ return false;
1272
+ }
1273
+ }
1274
+ return true;
1275
+ }
1276
+ /**
1277
+ * Checks if two arrays contain the same elements (order doesn't matter)
1278
+ * @param array1 - First array
1279
+ * @param array2 - Second array
1280
+ * @returns True if arrays contain same elements
1281
+ * @example
1282
+ * hasSameElements([1, 2, 3], [3, 2, 1]) // true
1283
+ * hasSameElements([1, 2], [1, 2, 2]) // false
1284
+ */
1285
+ export function hasSameElements(array1, array2) {
1286
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
1287
+ return false;
1288
+ }
1289
+ if (array1.length !== array2.length) {
1290
+ return false;
1291
+ }
1292
+ const freq1 = frequencies(array1);
1293
+ const freq2 = frequencies(array2);
1294
+ if (freq1.size !== freq2.size) {
1295
+ return false;
1296
+ }
1297
+ for (const [key, count] of freq1) {
1298
+ if (freq2.get(key) !== count) {
1299
+ return false;
1300
+ }
1301
+ }
1302
+ return true;
1303
+ }
1304
+ /**
1305
+ * Checks if array1 is a subset of array2 (all elements in array1 exist in array2)
1306
+ * @param array1 - Subset array
1307
+ * @param array2 - Superset array
1308
+ * @returns True if array1 is a subset of array2
1309
+ * @example
1310
+ * isSubset([1, 2], [1, 2, 3, 4]) // true
1311
+ * isSubset([1, 5], [1, 2, 3, 4]) // false
1312
+ */
1313
+ export function isSubset(array1, array2) {
1314
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
1315
+ return false;
1316
+ }
1317
+ const set2 = new Set(array2);
1318
+ return array1.every((item) => set2.has(item));
1319
+ }
1320
+ /**
1321
+ * Checks if array1 is a superset of array2 (all elements in array2 exist in array1)
1322
+ * @param array1 - Superset array
1323
+ * @param array2 - Subset array
1324
+ * @returns True if array1 is a superset of array2
1325
+ * @example
1326
+ * isSuperset([1, 2, 3, 4], [1, 2]) // true
1327
+ * isSuperset([1, 2, 3], [1, 5]) // false
1328
+ */
1329
+ export function isSuperset(array1, array2) {
1330
+ return isSubset(array2, array1);
1331
+ }
1332
+ /**
1333
+ * Interleaves multiple arrays (takes one element from each in turn)
1334
+ * @param arrays - Arrays to interleave
1335
+ * @returns Interleaved array
1336
+ * @example
1337
+ * interleave([1, 2], ['a', 'b'], [true, false]) // [1, 'a', true, 2, 'b', false]
1338
+ */
1339
+ export function interleave(...arrays) {
1340
+ if (arrays.length === 0) {
1341
+ return [];
1342
+ }
1343
+ const maxLength = Math.max(...arrays.map((arr) => arr.length));
1344
+ const result = [];
1345
+ for (let i = 0; i < maxLength; i++) {
1346
+ for (const arr of arrays) {
1347
+ if (i < arr.length) {
1348
+ result.push(arr[i]);
1349
+ }
1350
+ }
1351
+ }
1352
+ return result;
1353
+ }
1354
+ /**
1355
+ * Splits array into two arrays based on a predicate
1356
+ * Similar to partition but returns object with named properties
1357
+ * @param array - Source array
1358
+ * @param predicate - Function to test each element
1359
+ * @returns Object with 'pass' and 'fail' arrays
1360
+ * @example
1361
+ * bifurcate([1, 2, 3, 4], x => x % 2 === 0) // { pass: [2, 4], fail: [1, 3] }
1362
+ */
1363
+ export function bifurcate(array, predicate) {
1364
+ const pass = [];
1365
+ const fail = [];
1366
+ if (!Array.isArray(array)) {
1367
+ return { pass, fail };
1368
+ }
1369
+ for (let i = 0; i < array.length; i++) {
1370
+ if (predicate(array[i], i)) {
1371
+ pass.push(array[i]);
1372
+ }
1373
+ else {
1374
+ fail.push(array[i]);
1375
+ }
1376
+ }
1377
+ return { pass, fail };
1378
+ }
1379
+ /**
1380
+ * Creates an array with a specified length filled with values from a generator function
1381
+ * @param length - Length of array
1382
+ * @param generator - Function that generates value for each index
1383
+ * @returns Generated array
1384
+ * @example
1385
+ * generate(5, i => i * 2) // [0, 2, 4, 6, 8]
1386
+ * generate(3, () => Math.random()) // [0.123, 0.456, 0.789]
1387
+ */
1388
+ export function generate(length, generator) {
1389
+ if (length < 0) {
1390
+ return [];
1391
+ }
1392
+ const result = [];
1393
+ for (let i = 0; i < length; i++) {
1394
+ result.push(generator(i));
1395
+ }
1396
+ return result;
1397
+ }
1398
+ /**
1399
+ * Repeats an array n times
1400
+ * @param array - Source array
1401
+ * @param times - Number of times to repeat
1402
+ * @returns New array with repeated elements
1403
+ * @example
1404
+ * repeat([1, 2], 3) // [1, 2, 1, 2, 1, 2]
1405
+ */
1406
+ export function repeat(array, times) {
1407
+ if (!Array.isArray(array) || times < 0) {
1408
+ return [];
1409
+ }
1410
+ const result = [];
1411
+ for (let i = 0; i < times; i++) {
1412
+ result.push(...array);
1413
+ }
1414
+ return result;
1415
+ }
1416
+ /**
1417
+ * Finds the median value in a numeric array
1418
+ * @param array - Array of numbers
1419
+ * @returns Median value, or undefined if array is empty
1420
+ * @example
1421
+ * median([1, 2, 3, 4, 5]) // 3
1422
+ * median([1, 2, 3, 4]) // 2.5
1423
+ */
1424
+ export function median(array) {
1425
+ if (!Array.isArray(array) || array.length === 0) {
1426
+ return undefined;
1427
+ }
1428
+ const sorted = [...array].sort((a, b) => a - b);
1429
+ const mid = Math.floor(sorted.length / 2);
1430
+ if (sorted.length % 2 === 0) {
1431
+ return (sorted[mid - 1] + sorted[mid]) / 2;
1432
+ }
1433
+ return sorted[mid];
1434
+ }
1435
+ /**
1436
+ * Calculates the mode (most frequent value) in an array
1437
+ * @param array - Source array
1438
+ * @returns Most frequent value, or undefined if array is empty
1439
+ * @example
1440
+ * mode([1, 2, 2, 3, 3, 3]) // 3
1441
+ * mode(['a', 'b', 'b']) // 'b'
1442
+ */
1443
+ export function mode(array) {
1444
+ const common = mostCommon(array, 1);
1445
+ return common.length > 0 ? common[0] : undefined;
1446
+ }
1447
+ /**
1448
+ * Removes all occurrences of specified values from array
1449
+ * @param array - Source array
1450
+ * @param values - Values to remove
1451
+ * @returns New array without specified values
1452
+ * @example
1453
+ * without([1, 2, 3, 2, 4], 2, 3) // [1, 4]
1454
+ */
1455
+ export function without(array, ...values) {
1456
+ if (!Array.isArray(array)) {
1457
+ return [];
1458
+ }
1459
+ const excludeSet = new Set(values);
1460
+ return array.filter((item) => !excludeSet.has(item));
1461
+ }
1462
+ /**
1463
+ * Creates array of unique values that are in the first array but not in other arrays
1464
+ * @param array - Source array
1465
+ * @param others - Arrays to exclude
1466
+ * @returns Array with values only in first array
1467
+ * @example
1468
+ * differenceWith([1, 2, 3], [2], [3]) // [1]
1469
+ */
1470
+ export function differenceWith(array, ...others) {
1471
+ if (!Array.isArray(array)) {
1472
+ return [];
1473
+ }
1474
+ const excludeSet = new Set(others.flat());
1475
+ return array.filter((item) => !excludeSet.has(item));
1476
+ }
1477
+ /**
1478
+ * Groups consecutive identical values together
1479
+ * @param array - Source array
1480
+ * @returns Array of arrays with consecutive identical values
1481
+ * @example
1482
+ * groupConsecutive([1, 1, 2, 2, 2, 3, 1]) // [[1, 1], [2, 2, 2], [3], [1]]
1483
+ */
1484
+ export function groupConsecutive(array) {
1485
+ if (!Array.isArray(array) || array.length === 0) {
1486
+ return [];
1487
+ }
1488
+ const result = [];
1489
+ let currentGroup = [array[0]];
1490
+ for (let i = 1; i < array.length; i++) {
1491
+ if (array[i] === array[i - 1]) {
1492
+ currentGroup.push(array[i]);
1493
+ }
1494
+ else {
1495
+ result.push(currentGroup);
1496
+ currentGroup = [array[i]];
1497
+ }
1498
+ }
1499
+ result.push(currentGroup);
1500
+ return result;
1501
+ }
1502
+ /**
1503
+ * Removes consecutive duplicate values
1504
+ * @param array - Source array
1505
+ * @returns Array without consecutive duplicates
1506
+ * @example
1507
+ * dedupeConsecutive([1, 1, 2, 2, 2, 3, 1]) // [1, 2, 3, 1]
1508
+ */
1509
+ export function dedupeConsecutive(array) {
1510
+ if (!Array.isArray(array) || array.length === 0) {
1511
+ return [];
1512
+ }
1513
+ const result = [array[0]];
1514
+ for (let i = 1; i < array.length; i++) {
1515
+ if (array[i] !== array[i - 1]) {
1516
+ result.push(array[i]);
1517
+ }
1518
+ }
1519
+ return result;
1520
+ }
1521
+ /**
1522
+ * Assigns ranks to array elements (1-based, lower values get lower ranks)
1523
+ * @param array - Array of numbers
1524
+ * @param method - Ranking method: 'standard', 'dense', or 'ordinal'
1525
+ * @returns Array of ranks corresponding to input array
1526
+ * @example
1527
+ * rank([10, 20, 20, 30]) // [1, 2, 2, 4] (standard)
1528
+ * rank([10, 20, 20, 30], 'dense') // [1, 2, 2, 3]
1529
+ * rank([10, 20, 20, 30], 'ordinal') // [1, 2, 3, 4]
1530
+ */
1531
+ export function rank(array, method = 'standard') {
1532
+ if (!Array.isArray(array) || array.length === 0) {
1533
+ return [];
1534
+ }
1535
+ const indexed = array.map((value, index) => ({ value, index }));
1536
+ indexed.sort((a, b) => a.value - b.value);
1537
+ const ranks = new Array(array.length);
1538
+ if (method === 'ordinal') {
1539
+ for (let i = 0; i < indexed.length; i++) {
1540
+ ranks[indexed[i].index] = i + 1;
1541
+ }
1542
+ }
1543
+ else if (method === 'dense') {
1544
+ let denseRank = 1;
1545
+ ranks[indexed[0].index] = denseRank;
1546
+ for (let i = 1; i < indexed.length; i++) {
1547
+ if (indexed[i].value !== indexed[i - 1].value) {
1548
+ denseRank++;
1549
+ }
1550
+ ranks[indexed[i].index] = denseRank;
1551
+ }
1552
+ }
1553
+ else {
1554
+ // standard
1555
+ let i = 0;
1556
+ while (i < indexed.length) {
1557
+ let j = i;
1558
+ // Find all elements with same value
1559
+ while (j < indexed.length && indexed[j].value === indexed[i].value) {
1560
+ j++;
1561
+ }
1562
+ // Assign average rank to all tied elements
1563
+ const rankValue = i + 1;
1564
+ for (let k = i; k < j; k++) {
1565
+ ranks[indexed[k].index] = rankValue;
1566
+ }
1567
+ i = j;
1568
+ }
1569
+ }
1570
+ return ranks;
1571
+ }
1572
+ /**
1573
+ * Calculates the percentile value from a sorted or unsorted array
1574
+ * @param array - Array of numbers
1575
+ * @param percentile - Percentile to calculate (0-100)
1576
+ * @returns Percentile value, or undefined if array is empty
1577
+ * @example
1578
+ * percentile([1, 2, 3, 4, 5], 50) // 3 (median)
1579
+ * percentile([1, 2, 3, 4, 5, 6], 75) // 4.75
1580
+ */
1581
+ export function percentile(array, percentile) {
1582
+ if (!Array.isArray(array) || array.length === 0) {
1583
+ return undefined;
1584
+ }
1585
+ if (percentile < 0 || percentile > 100) {
1586
+ return undefined;
1587
+ }
1588
+ const sorted = [...array].sort((a, b) => a - b);
1589
+ const index = (percentile / 100) * (sorted.length - 1);
1590
+ const lower = Math.floor(index);
1591
+ const upper = Math.ceil(index);
1592
+ const weight = index - lower;
1593
+ if (lower === upper) {
1594
+ return sorted[lower];
1595
+ }
1596
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
1597
+ }
1598
+ /**
1599
+ * Calculates quartiles (Q1, Q2/median, Q3) of an array
1600
+ * @param array - Array of numbers
1601
+ * @returns Object with q1, q2 (median), q3, and iqr (interquartile range)
1602
+ * @example
1603
+ * quartiles([1, 2, 3, 4, 5]) // { q1: 1.5, q2: 3, q3: 4.5, iqr: 3 }
1604
+ */
1605
+ export function quartiles(array) {
1606
+ if (!Array.isArray(array) || array.length === 0) {
1607
+ return undefined;
1608
+ }
1609
+ const q1 = percentile(array, 25);
1610
+ const q2 = percentile(array, 50);
1611
+ const q3 = percentile(array, 75);
1612
+ return {
1613
+ q1,
1614
+ q2,
1615
+ q3,
1616
+ iqr: q3 - q1,
1617
+ };
1618
+ }
1619
+ /**
1620
+ * Calculates standard deviation of numeric array
1621
+ * @param array - Array of numbers
1622
+ * @param sample - If true, calculates sample standard deviation (default: false for population)
1623
+ * @returns Standard deviation, or undefined if array is empty
1624
+ * @example
1625
+ * standardDeviation([2, 4, 4, 4, 5, 5, 7, 9]) // 2
1626
+ */
1627
+ export function standardDeviation(array, sample = false) {
1628
+ if (!Array.isArray(array) || array.length === 0) {
1629
+ return undefined;
1630
+ }
1631
+ if (sample && array.length === 1) {
1632
+ return undefined;
1633
+ }
1634
+ const avg = array.reduce((sum, val) => sum + val, 0) / array.length;
1635
+ const squareDiffs = array.map((value) => Math.pow(value - avg, 2));
1636
+ const avgSquareDiff = squareDiffs.reduce((sum, val) => sum + val, 0) /
1637
+ (sample ? array.length - 1 : array.length);
1638
+ return Math.sqrt(avgSquareDiff);
1639
+ }
1640
+ /**
1641
+ * Calculates variance of numeric array
1642
+ * @param array - Array of numbers
1643
+ * @param sample - If true, calculates sample variance (default: false for population)
1644
+ * @returns Variance, or undefined if array is empty
1645
+ * @example
1646
+ * variance([2, 4, 4, 4, 5, 5, 7, 9]) // 4
1647
+ */
1648
+ export function variance(array, sample = false) {
1649
+ const std = standardDeviation(array, sample);
1650
+ return std !== undefined ? Math.pow(std, 2) : undefined;
1651
+ }
1652
+ /**
1653
+ * Normalizes array values to 0-1 range (min-max normalization)
1654
+ * @param array - Array of numbers
1655
+ * @returns Normalized array
1656
+ * @example
1657
+ * normalize([1, 2, 3, 4, 5]) // [0, 0.25, 0.5, 0.75, 1]
1658
+ */
1659
+ export function normalize(array) {
1660
+ if (!Array.isArray(array) || array.length === 0) {
1661
+ return [];
1662
+ }
1663
+ const min = Math.min(...array);
1664
+ const max = Math.max(...array);
1665
+ const range = max - min;
1666
+ if (range === 0) {
1667
+ return array.map(() => 0);
1668
+ }
1669
+ return array.map((value) => (value - min) / range);
1670
+ }
1671
+ /**
1672
+ * Standardizes array values (z-score normalization)
1673
+ * @param array - Array of numbers
1674
+ * @returns Standardized array with mean 0 and standard deviation 1
1675
+ * @example
1676
+ * standardize([2, 4, 4, 4, 5, 5, 7, 9]) // [-1.5, -0.5, -0.5, -0.5, 0, 0, 1, 2]
1677
+ */
1678
+ export function standardize(array) {
1679
+ if (!Array.isArray(array) || array.length === 0) {
1680
+ return [];
1681
+ }
1682
+ const avg = array.reduce((sum, val) => sum + val, 0) / array.length;
1683
+ const std = standardDeviation(array);
1684
+ if (std === undefined || std === 0) {
1685
+ return array.map(() => 0);
1686
+ }
1687
+ return array.map((value) => (value - avg) / std);
1688
+ }
1689
+ /**
1690
+ * Applies a function cumulatively to array elements (like reduce but returns all intermediate results)
1691
+ * @param array - Source array
1692
+ * @param fn - Accumulator function
1693
+ * @param initial - Initial value
1694
+ * @returns Array of cumulative results
1695
+ * @example
1696
+ * scan([1, 2, 3, 4], (acc, x) => acc + x, 0) // [1, 3, 6, 10]
1697
+ */
1698
+ export function scan(array, fn, initial) {
1699
+ if (!Array.isArray(array)) {
1700
+ return [];
1701
+ }
1702
+ const result = [];
1703
+ let accumulator = initial;
1704
+ for (let i = 0; i < array.length; i++) {
1705
+ accumulator = fn(accumulator, array[i], i);
1706
+ result.push(accumulator);
1707
+ }
1708
+ return result;
1709
+ }
1710
+ /**
1711
+ * Creates cumulative sum array
1712
+ * @param array - Array of numbers
1713
+ * @returns Array of cumulative sums
1714
+ * @example
1715
+ * cumulativeSum([1, 2, 3, 4]) // [1, 3, 6, 10]
1716
+ */
1717
+ export function cumulativeSum(array) {
1718
+ return scan(array, (acc, x) => acc + x, 0);
1719
+ }
1720
+ /**
1721
+ * Creates array of differences between consecutive elements
1722
+ * @param array - Array of numbers
1723
+ * @returns Array of differences
1724
+ * @example
1725
+ * diff([1, 3, 6, 10]) // [2, 3, 4]
1726
+ */
1727
+ export function diff(array) {
1728
+ if (!Array.isArray(array) || array.length < 2) {
1729
+ return [];
1730
+ }
1731
+ const result = [];
1732
+ for (let i = 1; i < array.length; i++) {
1733
+ result.push(array[i] - array[i - 1]);
1734
+ }
1735
+ return result;
1736
+ }
1737
+ /**
1738
+ * Pads array to specified length with a value
1739
+ * @param array - Source array
1740
+ * @param length - Target length
1741
+ * @param value - Value to pad with
1742
+ * @param position - Where to add padding: 'start' or 'end' (default: 'end')
1743
+ * @returns Padded array
1744
+ * @example
1745
+ * pad([1, 2, 3], 5, 0) // [1, 2, 3, 0, 0]
1746
+ * pad([1, 2, 3], 5, 0, 'start') // [0, 0, 1, 2, 3]
1747
+ */
1748
+ export function pad(array, length, value, position = 'end') {
1749
+ if (!Array.isArray(array) || length <= array.length) {
1750
+ return [...array];
1751
+ }
1752
+ const padding = new Array(length - array.length).fill(value);
1753
+ return position === 'start' ? [...padding, ...array] : [...array, ...padding];
1754
+ }
1755
+ /**
1756
+ * Trims array to remove elements from start and end that match predicate
1757
+ * @param array - Source array
1758
+ * @param predicate - Function to test elements to trim
1759
+ * @returns Trimmed array
1760
+ * @example
1761
+ * trim([0, 0, 1, 2, 3, 0, 0], x => x === 0) // [1, 2, 3]
1762
+ */
1763
+ export function trim(array, predicate) {
1764
+ if (!Array.isArray(array) || array.length === 0) {
1765
+ return [];
1766
+ }
1767
+ let start = 0;
1768
+ let end = array.length - 1;
1769
+ while (start <= end && predicate(array[start])) {
1770
+ start++;
1771
+ }
1772
+ while (end >= start && predicate(array[end])) {
1773
+ end--;
1774
+ }
1775
+ return array.slice(start, end + 1);
1776
+ }
1777
+ /**
1778
+ * Creates pairs of adjacent elements
1779
+ * @param array - Source array
1780
+ * @returns Array of pairs
1781
+ * @example
1782
+ * pairwise([1, 2, 3, 4]) // [[1, 2], [2, 3], [3, 4]]
1783
+ */
1784
+ export function pairwise(array) {
1785
+ if (!Array.isArray(array) || array.length < 2) {
1786
+ return [];
1787
+ }
1788
+ const result = [];
1789
+ for (let i = 0; i < array.length - 1; i++) {
1790
+ result.push([array[i], array[i + 1]]);
1791
+ }
1792
+ return result;
1793
+ }
1794
+ /**
1795
+ * Creates array of index-value pairs
1796
+ * @param array - Source array
1797
+ * @returns Array of [index, value] tuples
1798
+ * @example
1799
+ * enumerate(['a', 'b', 'c']) // [[0, 'a'], [1, 'b'], [2, 'c']]
1800
+ */
1801
+ export function enumerate(array) {
1802
+ if (!Array.isArray(array)) {
1803
+ return [];
1804
+ }
1805
+ return array.map((value, index) => [index, value]);
1806
+ }
1807
+ /**
1808
+ * Applies different functions to array elements based on their index
1809
+ * @param array - Source array
1810
+ * @param functions - Array of functions to apply
1811
+ * @returns Array with functions applied
1812
+ * @example
1813
+ * applyByIndex([1, 2, 3], [x => x * 2, x => x + 10, x => x ** 2]) // [2, 12, 9]
1814
+ */
1815
+ export function applyByIndex(array, functions) {
1816
+ if (!Array.isArray(array)) {
1817
+ return [];
1818
+ }
1819
+ return array.map((item, index) => {
1820
+ const fn = functions[index % functions.length];
1821
+ return fn(item);
1822
+ });
1823
+ }
1824
+ /**
1825
+ * Rotates elements to left by n positions
1826
+ * @param array - Source array
1827
+ * @param n - Number of positions to rotate (default: 1)
1828
+ * @returns Rotated array
1829
+ * @example
1830
+ * rotateLeft([1, 2, 3, 4, 5], 2) // [3, 4, 5, 1, 2]
1831
+ */
1832
+ export function rotateLeft(array, n = 1) {
1833
+ if (!Array.isArray(array) || array.length === 0) {
1834
+ return [];
1835
+ }
1836
+ const len = array.length;
1837
+ const rotations = ((n % len) + len) % len;
1838
+ return [...array.slice(rotations), ...array.slice(0, rotations)];
1839
+ }
1840
+ /**
1841
+ * Rotates elements to right by n positions
1842
+ * @param array - Source array
1843
+ * @param n - Number of positions to rotate (default: 1)
1844
+ * @returns Rotated array
1845
+ * @example
1846
+ * rotateRight([1, 2, 3, 4, 5], 2) // [4, 5, 1, 2, 3]
1847
+ */
1848
+ export function rotateRight(array, n = 1) {
1849
+ return rotateLeft(array, -n);
1850
+ }
1851
+ /**
1852
+ * Swaps two elements in array by their indices
1853
+ * @param array - Source array
1854
+ * @param i - First index
1855
+ * @param j - Second index
1856
+ * @returns New array with swapped elements
1857
+ * @example
1858
+ * swap([1, 2, 3, 4], 0, 3) // [4, 2, 3, 1]
1859
+ */
1860
+ export function swap(array, i, j) {
1861
+ if (!Array.isArray(array) ||
1862
+ i < 0 ||
1863
+ j < 0 ||
1864
+ i >= array.length ||
1865
+ j >= array.length) {
1866
+ return [...array];
1867
+ }
1868
+ const result = [...array];
1869
+ [result[i], result[j]] = [result[j], result[i]];
1870
+ return result;
1871
+ }
1872
+ /**
1873
+ * Reverses array segments between specified indices
1874
+ * @param array - Source array
1875
+ * @param start - Start index (inclusive)
1876
+ * @param end - End index (inclusive)
1877
+ * @returns Array with reversed segment
1878
+ * @example
1879
+ * reverseSegment([1, 2, 3, 4, 5], 1, 3) // [1, 4, 3, 2, 5]
1880
+ */
1881
+ export function reverseSegment(array, start, end) {
1882
+ if (!Array.isArray(array) ||
1883
+ start < 0 ||
1884
+ end >= array.length ||
1885
+ start >= end) {
1886
+ return [...array];
1887
+ }
1888
+ const result = [...array];
1889
+ const segment = result.slice(start, end + 1).reverse();
1890
+ result.splice(start, end - start + 1, ...segment);
1891
+ return result;
1892
+ }
1893
+ /**
1894
+ * Checks if array is sorted in ascending order
1895
+ * @param array - Array to check
1896
+ * @param compareFn - Optional comparison function
1897
+ * @returns True if array is sorted
1898
+ * @example
1899
+ * isSorted([1, 2, 3, 4]) // true
1900
+ * isSorted([1, 3, 2, 4]) // false
1901
+ */
1902
+ export function isSorted(array, compareFn) {
1903
+ if (!Array.isArray(array) || array.length <= 1) {
1904
+ return true;
1905
+ }
1906
+ const compare = compareFn || ((a, b) => (a < b ? -1 : a > b ? 1 : 0));
1907
+ for (let i = 1; i < array.length; i++) {
1908
+ if (compare(array[i - 1], array[i]) > 0) {
1909
+ return false;
1910
+ }
1911
+ }
1912
+ return true;
1913
+ }
1914
+ /**
1915
+ * Checks if array contains duplicates
1916
+ * @param array - Array to check
1917
+ * @returns True if array has duplicates
1918
+ * @example
1919
+ * hasDuplicates([1, 2, 3]) // false
1920
+ * hasDuplicates([1, 2, 2, 3]) // true
1921
+ */
1922
+ export function hasDuplicates(array) {
1923
+ if (!Array.isArray(array)) {
1924
+ return false;
1925
+ }
1926
+ return new Set(array).size !== array.length;
1927
+ }
1928
+ /**
1929
+ * Extracts values from array of objects by multiple keys
1930
+ * @param array - Array of objects
1931
+ * @param keys - Keys to extract
1932
+ * @returns Array of objects with only specified keys
1933
+ * @example
1934
+ * pickMany([{a: 1, b: 2, c: 3}], ['a', 'c']) // [{a: 1, c: 3}]
1935
+ */
1936
+ export function pickMany(array, keys) {
1937
+ if (!Array.isArray(array)) {
1938
+ return [];
1939
+ }
1940
+ return array.map((obj) => {
1941
+ const result = {};
1942
+ for (const key of keys) {
1943
+ if (key in obj) {
1944
+ result[key] = obj[key];
1945
+ }
1946
+ }
1947
+ return result;
1948
+ });
1949
+ }
1950
+ /**
1951
+ * Maps array to object using key and value selectors
1952
+ * @param array - Source array
1953
+ * @param keySelector - Function to extract key
1954
+ * @param valueSelector - Function to extract value (optional, defaults to identity)
1955
+ * @returns Object with mapped keys and values
1956
+ * @example
1957
+ * toObject([{id: 1, name: 'A'}], x => x.id, x => x.name) // {1: 'A'}
1958
+ */
1959
+ export function toObject(array, keySelector, valueSelector) {
1960
+ if (!Array.isArray(array)) {
1961
+ return {};
1962
+ }
1963
+ const result = {};
1964
+ for (let i = 0; i < array.length; i++) {
1965
+ const key = keySelector(array[i], i);
1966
+ const value = valueSelector
1967
+ ? valueSelector(array[i], i)
1968
+ : array[i];
1969
+ result[key] = value;
1970
+ }
1971
+ return result;
1972
+ }
1973
+ /**
1974
+ * Maps array elements asynchronously in sequence
1975
+ * @param array - Source array
1976
+ * @param fn - Async mapping function
1977
+ * @returns Promise resolving to mapped array
1978
+ * @example
1979
+ * await mapAsync([1, 2, 3], async x => await fetchData(x))
1980
+ */
1981
+ export async function mapAsync(array, fn) {
1982
+ if (!Array.isArray(array)) {
1983
+ return [];
1984
+ }
1985
+ const result = [];
1986
+ for (let i = 0; i < array.length; i++) {
1987
+ result.push(await fn(array[i], i));
1988
+ }
1989
+ return result;
1990
+ }
1991
+ /**
1992
+ * Maps array elements asynchronously in parallel
1993
+ * @param array - Source array
1994
+ * @param fn - Async mapping function
1995
+ * @returns Promise resolving to mapped array
1996
+ * @example
1997
+ * await mapAsyncParallel([1, 2, 3], async x => await fetchData(x))
1998
+ */
1999
+ export async function mapAsyncParallel(array, fn) {
2000
+ if (!Array.isArray(array)) {
2001
+ return [];
2002
+ }
2003
+ return Promise.all(array.map((item, index) => fn(item, index)));
2004
+ }
2005
+ /**
2006
+ * Filters array elements asynchronously
2007
+ * @param array - Source array
2008
+ * @param predicate - Async predicate function
2009
+ * @returns Promise resolving to filtered array
2010
+ * @example
2011
+ * await filterAsync([1, 2, 3], async x => await isValid(x))
2012
+ */
2013
+ export async function filterAsync(array, predicate) {
2014
+ if (!Array.isArray(array)) {
2015
+ return [];
2016
+ }
2017
+ const result = [];
2018
+ for (let i = 0; i < array.length; i++) {
2019
+ if (await predicate(array[i], i)) {
2020
+ result.push(array[i]);
2021
+ }
2022
+ }
2023
+ return result;
2024
+ }
2025
+ /**
2026
+ * Reduces array asynchronously
2027
+ * @param array - Source array
2028
+ * @param fn - Async reducer function
2029
+ * @param initial - Initial accumulator value
2030
+ * @returns Promise resolving to final accumulator value
2031
+ * @example
2032
+ * await reduceAsync([1, 2, 3], async (acc, x) => acc + await process(x), 0)
2033
+ */
2034
+ export async function reduceAsync(array, fn, initial) {
2035
+ if (!Array.isArray(array)) {
2036
+ return initial;
2037
+ }
2038
+ let accumulator = initial;
2039
+ for (let i = 0; i < array.length; i++) {
2040
+ accumulator = await fn(accumulator, array[i], i);
2041
+ }
2042
+ return accumulator;
2043
+ }
2044
+ /**
2045
+ * Finds first element that satisfies async predicate
2046
+ * @param array - Source array
2047
+ * @param predicate - Async predicate function
2048
+ * @returns Promise resolving to found element or undefined
2049
+ * @example
2050
+ * await findAsync([1, 2, 3], async x => await isValid(x))
2051
+ */
2052
+ export async function findAsync(array, predicate) {
2053
+ if (!Array.isArray(array)) {
2054
+ return undefined;
2055
+ }
2056
+ for (let i = 0; i < array.length; i++) {
2057
+ if (await predicate(array[i], i)) {
2058
+ return array[i];
2059
+ }
2060
+ }
2061
+ return undefined;
2062
+ }
2063
+ /**
2064
+ * Checks if some element satisfies async predicate
2065
+ * @param array - Source array
2066
+ * @param predicate - Async predicate function
2067
+ * @returns Promise resolving to true if any element matches
2068
+ * @example
2069
+ * await someAsync([1, 2, 3], async x => await isValid(x))
2070
+ */
2071
+ export async function someAsync(array, predicate) {
2072
+ if (!Array.isArray(array)) {
2073
+ return false;
2074
+ }
2075
+ for (let i = 0; i < array.length; i++) {
2076
+ if (await predicate(array[i], i)) {
2077
+ return true;
2078
+ }
2079
+ }
2080
+ return false;
2081
+ }
2082
+ /**
2083
+ * Checks if every element satisfies async predicate
2084
+ * @param array - Source array
2085
+ * @param predicate - Async predicate function
2086
+ * @returns Promise resolving to true if all elements match
2087
+ * @example
2088
+ * await everyAsync([1, 2, 3], async x => await isValid(x))
2089
+ */
2090
+ export async function everyAsync(array, predicate) {
2091
+ if (!Array.isArray(array)) {
2092
+ return true;
2093
+ }
2094
+ for (let i = 0; i < array.length; i++) {
2095
+ if (!(await predicate(array[i], i))) {
2096
+ return false;
2097
+ }
2098
+ }
2099
+ return true;
2100
+ }
2101
+ /**
2102
+ * Calculates weighted average from array of {value, weight} objects or using selectors
2103
+ * @param array - Source array
2104
+ * @param valueSelector - Function to extract value
2105
+ * @param weightSelector - Function to extract weight
2106
+ * @returns Weighted average, or 0 if total weight is 0
2107
+ * @example
2108
+ * weightedAverage([{score: 80, credits: 3}, {score: 90, credits: 2}], x => x.score, x => x.credits) // 84
2109
+ */
2110
+ export function weightedAverage(array, valueSelector, weightSelector) {
2111
+ if (!Array.isArray(array) || array.length === 0) {
2112
+ return 0;
2113
+ }
2114
+ let totalWeight = 0;
2115
+ let weightedSum = 0;
2116
+ for (const item of array) {
2117
+ const value = typeof valueSelector === 'function'
2118
+ ? valueSelector(item)
2119
+ : get(item, valueSelector);
2120
+ const weight = typeof weightSelector === 'function'
2121
+ ? weightSelector(item)
2122
+ : get(item, weightSelector);
2123
+ if (typeof value === 'number' && typeof weight === 'number') {
2124
+ weightedSum += value * weight;
2125
+ totalWeight += weight;
2126
+ }
2127
+ }
2128
+ return totalWeight === 0 ? 0 : weightedSum / totalWeight;
2129
+ }
2130
+ /**
2131
+ * Randomly selects elements with weights
2132
+ * @param array - Source array
2133
+ * @param weightSelector - Function to extract weight
2134
+ * @param count - Number of elements to select (default: 1)
2135
+ * @returns Array of selected elements
2136
+ * @example
2137
+ * weightedSample([{item: 'A', prob: 0.7}, {item: 'B', prob: 0.3}], x => x.prob, 10)
2138
+ */
2139
+ export function weightedSample(array, weightSelector, count = 1) {
2140
+ if (!Array.isArray(array) || array.length === 0 || count < 1) {
2141
+ return [];
2142
+ }
2143
+ // Calculate cumulative weights
2144
+ const weights = [];
2145
+ let totalWeight = 0;
2146
+ for (const item of array) {
2147
+ const weight = typeof weightSelector === 'function'
2148
+ ? weightSelector(item)
2149
+ : get(item, weightSelector);
2150
+ totalWeight += typeof weight === 'number' && weight > 0 ? weight : 0;
2151
+ weights.push(totalWeight);
2152
+ }
2153
+ if (totalWeight === 0) {
2154
+ return [];
2155
+ }
2156
+ const result = [];
2157
+ for (let i = 0; i < count; i++) {
2158
+ const random = Math.random() * totalWeight;
2159
+ const index = weights.findIndex((w) => random < w);
2160
+ if (index !== -1) {
2161
+ result.push(array[index]);
2162
+ }
2163
+ }
2164
+ return result;
2165
+ }
2166
+ /**
2167
+ * Splits array into segments of varying sizes
2168
+ * @param array - Source array
2169
+ * @param sizes - Array of segment sizes
2170
+ * @returns Array of segments
2171
+ * @example
2172
+ * segment([1, 2, 3, 4, 5, 6], [2, 3, 1]) // [[1, 2], [3, 4, 5], [6]]
2173
+ */
2174
+ export function segment(array, sizes) {
2175
+ if (!Array.isArray(array) || !Array.isArray(sizes)) {
2176
+ return [];
2177
+ }
2178
+ const result = [];
2179
+ let offset = 0;
2180
+ for (const size of sizes) {
2181
+ if (offset >= array.length)
2182
+ break;
2183
+ result.push(array.slice(offset, offset + size));
2184
+ offset += size;
2185
+ }
2186
+ return result;
2187
+ }
2188
+ /**
2189
+ * Creates array with elements that appear in first array specified number of times
2190
+ * @param array - Source array
2191
+ * @param count - Required occurrence count
2192
+ * @returns Array with elements appearing exactly count times
2193
+ * @example
2194
+ * selectByCount([1, 1, 2, 2, 2, 3], 2) // [1, 1, 2, 2]
2195
+ */
2196
+ export function selectByCount(array, count) {
2197
+ if (!Array.isArray(array) || count < 1) {
2198
+ return [];
2199
+ }
2200
+ const freq = frequencies(array);
2201
+ const result = [];
2202
+ for (const item of array) {
2203
+ if (freq.get(item) === count) {
2204
+ result.push(item);
2205
+ }
2206
+ }
2207
+ return result;
2208
+ }
2209
+ /**
2210
+ * Filters array to keep only elements that pass predicate n consecutive times
2211
+ * @param array - Source array
2212
+ * @param n - Number of consecutive passes required
2213
+ * @param predicate - Predicate function
2214
+ * @returns Filtered array
2215
+ * @example
2216
+ * filterConsecutive([1, 2, 2, 3, 3, 3], 2, x => x > 1) // [2, 2, 3, 3, 3]
2217
+ */
2218
+ export function filterConsecutive(array, n, predicate) {
2219
+ if (!Array.isArray(array) || n < 1) {
2220
+ return [];
2221
+ }
2222
+ const result = [];
2223
+ let consecutiveCount = 0;
2224
+ const buffer = [];
2225
+ for (const item of array) {
2226
+ if (predicate(item)) {
2227
+ consecutiveCount++;
2228
+ buffer.push(item);
2229
+ if (consecutiveCount >= n) {
2230
+ result.push(...buffer.splice(0, buffer.length - n + 1));
2231
+ }
2232
+ }
2233
+ else {
2234
+ consecutiveCount = 0;
2235
+ buffer.length = 0;
2236
+ }
2237
+ }
2238
+ if (consecutiveCount >= n) {
2239
+ result.push(...buffer);
2240
+ }
2241
+ return result;
2242
+ }
2243
+ /**
2244
+ * Maps each element using accumulated context from previous elements
2245
+ * @param array - Source array
2246
+ * @param fn - Mapping function that receives accumulated context
2247
+ * @param initialContext - Initial context value
2248
+ * @returns Array of mapped values
2249
+ * @example
2250
+ * mapWithContext([1, 2, 3], (x, ctx) => ({value: x + ctx, newCtx: ctx + x}), 0)
2251
+ * // [{value: 1, newCtx: 1}, {value: 3, newCtx: 3}, {value: 6, newCtx: 6}]
2252
+ */
2253
+ export function mapWithContext(array, fn, initialContext) {
2254
+ if (!Array.isArray(array)) {
2255
+ return [];
2256
+ }
2257
+ const result = [];
2258
+ let context = initialContext;
2259
+ for (let i = 0; i < array.length; i++) {
2260
+ const { value, newContext } = fn(array[i], context, i);
2261
+ result.push(value);
2262
+ context = newContext;
2263
+ }
2264
+ return result;
2265
+ }
2266
+ /**
2267
+ * Groups array elements into fixed-size batches, with last batch potentially smaller
2268
+ * Similar to chunk but with better naming for batch processing scenarios
2269
+ * @param array - Source array
2270
+ * @param batchSize - Size of each batch
2271
+ * @returns Array of batches
2272
+ * @example
2273
+ * batch([1, 2, 3, 4, 5], 2) // [[1, 2], [3, 4], [5]]
2274
+ */
2275
+ export function batch(array, batchSize) {
2276
+ return chunk(array, batchSize);
2277
+ }
2278
+ /**
2279
+ * Applies a function to pairs of elements from two arrays
2280
+ * @param array1 - First array
2281
+ * @param array2 - Second array
2282
+ * @param fn - Function to combine elements
2283
+ * @returns Array of combined results
2284
+ * @example
2285
+ * zipWith([1, 2, 3], [4, 5, 6], (a, b) => a + b) // [5, 7, 9]
2286
+ */
2287
+ export function zipWith(array1, array2, fn) {
2288
+ if (!Array.isArray(array1) || !Array.isArray(array2)) {
2289
+ return [];
2290
+ }
2291
+ const length = Math.min(array1.length, array2.length);
2292
+ const result = [];
2293
+ for (let i = 0; i < length; i++) {
2294
+ result.push(fn(array1[i], array2[i]));
2295
+ }
2296
+ return result;
2297
+ }
2298
+ /**
2299
+ * Merges multiple arrays of objects by a key, with custom merge strategy
2300
+ * @param arrays - Arrays to merge
2301
+ * @param key - Key to merge by
2302
+ * @param mergeStrategy - How to merge: 'first', 'last', 'concat', or custom function
2303
+ * @returns Merged array
2304
+ * @example
2305
+ * mergeArraysByKey(
2306
+ * [[{id: 1, a: 1}], [{id: 1, b: 2}]],
2307
+ * 'id',
2308
+ * (existing, incoming) => ({...existing, ...incoming})
2309
+ * )
2310
+ */
2311
+ export function mergeArraysByKey(arrays, key, mergeStrategy = 'last') {
2312
+ if (!Array.isArray(arrays) || arrays.length === 0) {
2313
+ return [];
2314
+ }
2315
+ const map = new Map();
2316
+ for (const array of arrays) {
2317
+ if (!Array.isArray(array))
2318
+ continue;
2319
+ for (const item of array) {
2320
+ const keyValue = get(item, key);
2321
+ if (mergeStrategy === 'first') {
2322
+ if (!map.has(keyValue)) {
2323
+ map.set(keyValue, item);
2324
+ }
2325
+ }
2326
+ else if (mergeStrategy === 'last') {
2327
+ map.set(keyValue, item);
2328
+ }
2329
+ else if (mergeStrategy === 'concat') {
2330
+ const existing = map.get(keyValue);
2331
+ if (Array.isArray(existing)) {
2332
+ existing.push(item);
2333
+ }
2334
+ else if (existing) {
2335
+ map.set(keyValue, [existing, item]);
2336
+ }
2337
+ else {
2338
+ map.set(keyValue, item);
2339
+ }
2340
+ }
2341
+ else if (typeof mergeStrategy === 'function') {
2342
+ const existing = map.get(keyValue);
2343
+ if (existing && !Array.isArray(existing)) {
2344
+ map.set(keyValue, mergeStrategy(existing, item));
2345
+ }
2346
+ else {
2347
+ map.set(keyValue, item);
2348
+ }
2349
+ }
2350
+ }
2351
+ }
2352
+ const result = [];
2353
+ for (const value of map.values()) {
2354
+ if (Array.isArray(value) && mergeStrategy === 'concat') {
2355
+ result.push(...value);
2356
+ }
2357
+ else if (!Array.isArray(value)) {
2358
+ result.push(value);
2359
+ }
2360
+ }
2361
+ return result;
2362
+ }
2363
+ /**
2364
+ * Creates a lookup/index object for fast access by key
2365
+ * @param array - Array of objects
2366
+ * @param key - Key to index by
2367
+ * @returns Object mapping key values to array elements
2368
+ * @example
2369
+ * indexBy([{id: 1, name: 'A'}, {id: 2, name: 'B'}], 'id')
2370
+ * // {1: {id: 1, name: 'A'}, 2: {id: 2, name: 'B'}}
2371
+ */
2372
+ export function indexBy(array, key) {
2373
+ return toObject(array, (item) => String(get(item, key)));
2374
+ }
2375
+ /**
2376
+ * Performs binary search on sorted array
2377
+ * @param array - Sorted array
2378
+ * @param target - Value to search for
2379
+ * @param compareFn - Optional comparison function
2380
+ * @returns Index of target, or -1 if not found
2381
+ * @example
2382
+ * binarySearch([1, 2, 3, 4, 5], 3) // 2
2383
+ * binarySearch([1, 2, 4, 5], 3) // -1
2384
+ */
2385
+ export function binarySearch(array, target, compareFn) {
2386
+ if (!Array.isArray(array) || array.length === 0) {
2387
+ return -1;
2388
+ }
2389
+ const compare = compareFn || ((a, b) => (a < b ? -1 : a > b ? 1 : 0));
2390
+ let left = 0;
2391
+ let right = array.length - 1;
2392
+ while (left <= right) {
2393
+ const mid = Math.floor((left + right) / 2);
2394
+ const comparison = compare(array[mid], target);
2395
+ if (comparison === 0) {
2396
+ return mid;
2397
+ }
2398
+ else if (comparison < 0) {
2399
+ left = mid + 1;
2400
+ }
2401
+ else {
2402
+ right = mid - 1;
2403
+ }
2404
+ }
2405
+ return -1;
2406
+ }
2407
+ /**
2408
+ * Inserts value into sorted array maintaining sort order
2409
+ * @param array - Sorted array
2410
+ * @param value - Value to insert
2411
+ * @param compareFn - Optional comparison function
2412
+ * @returns New sorted array with value inserted
2413
+ * @example
2414
+ * insertSorted([1, 3, 5, 7], 4) // [1, 3, 4, 5, 7]
2415
+ */
2416
+ export function insertSorted(array, value, compareFn) {
2417
+ if (!Array.isArray(array)) {
2418
+ return [value];
2419
+ }
2420
+ const compare = compareFn || ((a, b) => (a < b ? -1 : a > b ? 1 : 0));
2421
+ const result = [...array];
2422
+ let insertIndex = 0;
2423
+ while (insertIndex < result.length &&
2424
+ compare(result[insertIndex], value) < 0) {
2425
+ insertIndex++;
2426
+ }
2427
+ result.splice(insertIndex, 0, value);
2428
+ return result;
2429
+ }