@rimbu/common 0.12.3 → 1.0.0-alpha.2

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 (139) hide show
  1. package/README.md +21 -4
  2. package/{src/async-optlazy.ts → dist/bun/async-optlazy.mts} +1 -1
  3. package/{src/async-reducer.ts → dist/bun/async-reducer.mts} +17 -8
  4. package/{src/collect.ts → dist/bun/collect.mts} +1 -1
  5. package/{src/comp.ts → dist/bun/comp.mts} +1 -1
  6. package/{src/index-range.ts → dist/bun/index-range.mts} +1 -1
  7. package/{src/index.ts → dist/bun/index.mts} +1 -1
  8. package/dist/bun/internal.mts +13 -0
  9. package/{src/reducer.ts → dist/bun/reducer.mts} +5 -3
  10. package/dist/cjs/async-optlazy.cjs +44 -0
  11. package/dist/cjs/async-reducer.cjs +1621 -0
  12. package/dist/cjs/collect.cjs +33 -0
  13. package/dist/cjs/comp.cjs +1621 -0
  14. package/dist/cjs/eq.cjs +210 -0
  15. package/dist/cjs/err.cjs +55 -0
  16. package/dist/cjs/index-range.cjs +114 -0
  17. package/dist/cjs/index.cjs +1685 -0
  18. package/dist/cjs/internal.cjs +1685 -0
  19. package/dist/cjs/optlazy.cjs +41 -0
  20. package/dist/cjs/range.cjs +52 -0
  21. package/dist/cjs/reducer.cjs +1621 -0
  22. package/dist/cjs/traverse-state.cjs +49 -0
  23. package/dist/cjs/types.cjs +18 -0
  24. package/dist/cjs/update.cjs +35 -0
  25. package/dist/{module/async-optlazy.js → esm/async-optlazy.mjs} +5 -8
  26. package/dist/esm/async-optlazy.mjs.map +1 -0
  27. package/dist/{module/async-reducer.js → esm/async-reducer.mjs} +67 -69
  28. package/dist/esm/async-reducer.mjs.map +1 -0
  29. package/dist/{module/collect.js → esm/collect.mjs} +1 -1
  30. package/dist/esm/collect.mjs.map +1 -0
  31. package/dist/{module/comp.js → esm/comp.mjs} +2 -2
  32. package/dist/esm/comp.mjs.map +1 -0
  33. package/dist/{module/eq.js → esm/eq.mjs} +1 -1
  34. package/dist/esm/eq.mjs.map +1 -0
  35. package/dist/{module/err.js → esm/err.mjs} +1 -1
  36. package/dist/esm/err.mjs.map +1 -0
  37. package/dist/{module/index-range.js → esm/index-range.mjs} +2 -3
  38. package/dist/esm/index-range.mjs.map +1 -0
  39. package/dist/esm/index.mjs +7 -0
  40. package/dist/esm/index.mjs.map +1 -0
  41. package/dist/esm/internal.mjs +14 -0
  42. package/dist/esm/internal.mjs.map +1 -0
  43. package/dist/{module/optlazy.js → esm/optlazy.mjs} +1 -1
  44. package/dist/esm/optlazy.mjs.map +1 -0
  45. package/dist/{module/range.js → esm/range.mjs} +1 -1
  46. package/dist/esm/range.mjs.map +1 -0
  47. package/dist/{module/reducer.js → esm/reducer.mjs} +10 -8
  48. package/dist/esm/reducer.mjs.map +1 -0
  49. package/dist/{module/traverse-state.js → esm/traverse-state.mjs} +1 -1
  50. package/dist/esm/traverse-state.mjs.map +1 -0
  51. package/dist/esm/types.mjs +2 -0
  52. package/dist/esm/types.mjs.map +1 -0
  53. package/dist/{module/update.js → esm/update.mjs} +1 -1
  54. package/dist/esm/update.mjs.map +1 -0
  55. package/dist/types/{async-optlazy.d.ts → async-optlazy.d.mts} +1 -1
  56. package/dist/types/{async-reducer.d.ts → async-reducer.d.mts} +2 -2
  57. package/dist/types/{collect.d.ts → collect.d.mts} +1 -1
  58. package/dist/types/{comp.d.ts → comp.d.mts} +1 -1
  59. package/dist/types/{index-range.d.ts → index-range.d.mts} +1 -1
  60. package/dist/types/{index.d.ts → index.d.mts} +1 -1
  61. package/dist/types/internal.d.mts +13 -0
  62. package/dist/types/{reducer.d.ts → reducer.d.mts} +1 -1
  63. package/package.json +27 -21
  64. package/src/async-optlazy.mts +49 -0
  65. package/src/async-reducer.mts +1157 -0
  66. package/src/collect.mts +43 -0
  67. package/src/comp.mts +625 -0
  68. package/src/eq.mts +406 -0
  69. package/src/err.mts +43 -0
  70. package/src/index-range.mts +120 -0
  71. package/{dist/module/index.js → src/index.mts} +2 -2
  72. package/src/internal.mts +13 -0
  73. package/{dist/main/optlazy.js → src/optlazy.mts} +24 -14
  74. package/src/range.mts +58 -0
  75. package/src/reducer.mts +1025 -0
  76. package/src/traverse-state.mts +59 -0
  77. package/src/types.mts +40 -0
  78. package/{dist/main/update.js → src/update.mts} +10 -10
  79. package/dist/main/async-optlazy.js +0 -48
  80. package/dist/main/async-optlazy.js.map +0 -1
  81. package/dist/main/async-reducer.js +0 -872
  82. package/dist/main/async-reducer.js.map +0 -1
  83. package/dist/main/collect.js +0 -11
  84. package/dist/main/collect.js.map +0 -1
  85. package/dist/main/comp.js +0 -542
  86. package/dist/main/comp.js.map +0 -1
  87. package/dist/main/eq.js +0 -382
  88. package/dist/main/eq.js.map +0 -1
  89. package/dist/main/err.js +0 -62
  90. package/dist/main/err.js.map +0 -1
  91. package/dist/main/index-range.js +0 -110
  92. package/dist/main/index-range.js.map +0 -1
  93. package/dist/main/index.js +0 -10
  94. package/dist/main/index.js.map +0 -1
  95. package/dist/main/internal.js +0 -17
  96. package/dist/main/internal.js.map +0 -1
  97. package/dist/main/optlazy.js.map +0 -1
  98. package/dist/main/range.js +0 -38
  99. package/dist/main/range.js.map +0 -1
  100. package/dist/main/reducer.js +0 -694
  101. package/dist/main/reducer.js.map +0 -1
  102. package/dist/main/traverse-state.js +0 -33
  103. package/dist/main/traverse-state.js.map +0 -1
  104. package/dist/main/types.js +0 -3
  105. package/dist/main/types.js.map +0 -1
  106. package/dist/main/update.js.map +0 -1
  107. package/dist/module/async-optlazy.js.map +0 -1
  108. package/dist/module/async-reducer.js.map +0 -1
  109. package/dist/module/collect.js.map +0 -1
  110. package/dist/module/comp.js.map +0 -1
  111. package/dist/module/eq.js.map +0 -1
  112. package/dist/module/err.js.map +0 -1
  113. package/dist/module/index-range.js.map +0 -1
  114. package/dist/module/index.js.map +0 -1
  115. package/dist/module/internal.js +0 -14
  116. package/dist/module/internal.js.map +0 -1
  117. package/dist/module/optlazy.js.map +0 -1
  118. package/dist/module/range.js.map +0 -1
  119. package/dist/module/reducer.js.map +0 -1
  120. package/dist/module/traverse-state.js.map +0 -1
  121. package/dist/module/types.js +0 -2
  122. package/dist/module/types.js.map +0 -1
  123. package/dist/module/update.js.map +0 -1
  124. package/dist/types/internal.d.ts +0 -13
  125. package/src/internal.ts +0 -13
  126. /package/{src/eq.ts → dist/bun/eq.mts} +0 -0
  127. /package/{src/err.ts → dist/bun/err.mts} +0 -0
  128. /package/{src/optlazy.ts → dist/bun/optlazy.mts} +0 -0
  129. /package/{src/range.ts → dist/bun/range.mts} +0 -0
  130. /package/{src/traverse-state.ts → dist/bun/traverse-state.mts} +0 -0
  131. /package/{src/types.ts → dist/bun/types.mts} +0 -0
  132. /package/{src/update.ts → dist/bun/update.mts} +0 -0
  133. /package/dist/types/{eq.d.ts → eq.d.mts} +0 -0
  134. /package/dist/types/{err.d.ts → err.d.mts} +0 -0
  135. /package/dist/types/{optlazy.d.ts → optlazy.d.mts} +0 -0
  136. /package/dist/types/{range.d.ts → range.d.mts} +0 -0
  137. /package/dist/types/{traverse-state.d.ts → traverse-state.d.mts} +0 -0
  138. /package/dist/types/{types.d.ts → types.d.mts} +0 -0
  139. /package/dist/types/{update.d.ts → update.d.mts} +0 -0
@@ -0,0 +1,43 @@
1
+ import type { MaybePromise } from './internal.mjs';
2
+
3
+ /**
4
+ * A function used in `collect` methods to collect values from a collection. This is basically a single-pass map and filter.
5
+ * @param value - the input value
6
+ * @param index - the index of the input value
7
+ * @param skip - a token that can be returned to skip the value
8
+ * @param halt - a function that, when called, ensures no more values are passed
9
+ * @returns either a new value to collect, or the `skip` token indicating to skip the value
10
+ */
11
+ export type CollectFun<T, R> = (
12
+ value: T,
13
+ index: number,
14
+ skip: CollectFun.Skip,
15
+ halt: () => void
16
+ ) => R | CollectFun.Skip;
17
+
18
+ export namespace CollectFun {
19
+ /**
20
+ * Indicates, when returned from a collect function, to skip the value.
21
+ */
22
+ export const Skip = Symbol('Skip');
23
+
24
+ /**
25
+ * Indicates, when returned from a collect function, to skip the value.
26
+ */
27
+ export type Skip = typeof Skip;
28
+ }
29
+
30
+ /**
31
+ * A potentially asynchronous function used in `collect` methods to collect values from a collection. This is basically a single-pass map and filter.
32
+ * @param value - the input value
33
+ * @param index - the index of the input value
34
+ * @param skip - a token that can be returned to skip the value
35
+ * @param halt - a function that, when called, ensures no more values are passed
36
+ * @returns either a new value to collect, or the `skip` token indicating to skip the value
37
+ */
38
+ export type AsyncCollectFun<T, R> = (
39
+ value: T,
40
+ index: number,
41
+ skip: CollectFun.Skip,
42
+ halt: () => void
43
+ ) => MaybePromise<R | CollectFun.Skip>;
package/src/comp.mts ADDED
@@ -0,0 +1,625 @@
1
+ import { Eq } from './internal.mjs';
2
+
3
+ /**
4
+ * An object providing methods to compare two values of type `K`.
5
+ * @typeparam K - the value type
6
+ */
7
+ export interface Comp<K> {
8
+ /**
9
+ * Returns 0 if given `value1` and `value2` are equal, a positive value is `value1` is greater than
10
+ * `value2`, and a negative value otherwise.
11
+ * @param value1 - the first value to compare
12
+ * @param value2 - the seconds value to compare
13
+ * @example
14
+ * ```ts
15
+ * const c = Comp.numberComp()
16
+ * console.log(c.compare(5, 5))
17
+ * // => 0
18
+ * console.log(c.compare(3, 5))
19
+ * // => -2
20
+ * console.log(c.compare(5, 3))
21
+ * // => 2
22
+ * ```
23
+ */
24
+ compare(value1: K, value2: K): number;
25
+ /**
26
+ * Returns true if this instance can compare given `obj`.
27
+ * @param obj - the object to check
28
+ * @example
29
+ * ```ts
30
+ * const c = Comp.numberComp()
31
+ * console.log(c.isComparable(5))
32
+ * // => true
33
+ * console.log(c.isComparable('a'))
34
+ * // => false
35
+ * ```
36
+ */
37
+ isComparable(obj: any): obj is K;
38
+ }
39
+
40
+ export namespace Comp {
41
+ const _anyFlatComp: Comp<any> = createAnyComp('FLAT');
42
+ const _anyShallowComp: Comp<any> = createAnyComp('SHALLOW');
43
+ const _anyDeepComp: Comp<any> = createAnyComp('DEEP');
44
+
45
+ /**
46
+ * Returns the default Comp instance, which is the Comp.anyDeepComp() instance.
47
+ */
48
+ export function defaultComp(): Comp<any> {
49
+ return _anyDeepComp;
50
+ }
51
+
52
+ const _numberComp: Comp<number> = {
53
+ isComparable(obj): obj is number {
54
+ return typeof obj === 'number';
55
+ },
56
+ compare(v1, v2) {
57
+ if (Number.isFinite(v1) && Number.isFinite(v2)) {
58
+ return v1 - v2;
59
+ }
60
+ if (Number.isNaN(v1)) {
61
+ if (Number.isNaN(v2)) return 0;
62
+ if (v2 === Number.POSITIVE_INFINITY) return 1;
63
+ if (v2 === Number.NEGATIVE_INFINITY) return -1;
64
+ return -1;
65
+ }
66
+
67
+ // only infinities remain
68
+ if (v1 === Number.POSITIVE_INFINITY) {
69
+ return v2 === Number.POSITIVE_INFINITY ? 0 : 1;
70
+ }
71
+
72
+ // v1 === Number.NEGATIVE_INFINITY
73
+ return v2 === Number.NEGATIVE_INFINITY ? 0 : -1;
74
+ },
75
+ };
76
+
77
+ /**
78
+ * Returns a default number Comp instance that orders numbers naturally.
79
+ * @example
80
+ * ```ts
81
+ * const c = Comp.numberComp();
82
+ * console.log(c.compare(3, 5))
83
+ * // => -2
84
+ * ```
85
+ */
86
+ export function numberComp(): Comp<number> {
87
+ return _numberComp;
88
+ }
89
+
90
+ const _booleanComp: Comp<boolean> = {
91
+ isComparable(obj): obj is boolean {
92
+ return typeof obj === 'boolean';
93
+ },
94
+ compare(v1, v2) {
95
+ return v1 === v2 ? 0 : v1 ? 1 : -1;
96
+ },
97
+ };
98
+
99
+ /**
100
+ * Returns a default boolean Comp instance that orders booleans according to false < true.
101
+ * @example
102
+ * ```ts
103
+ * const c = Comp.booleanComp();
104
+ * console.log(c.compare(false, true) < 0)
105
+ * // => true
106
+ * console.log(c.compare(true, true))
107
+ * // => 0
108
+ * ```
109
+ */
110
+ export function booleanComp(): Comp<boolean> {
111
+ return _booleanComp;
112
+ }
113
+
114
+ const _bigIntComp: Comp<bigint> = {
115
+ isComparable(obj): obj is bigint {
116
+ return typeof obj === 'bigint';
117
+ },
118
+ compare(v1, v2) {
119
+ const res = v1 - v2;
120
+ if (res > 0) return 1;
121
+ if (res < 0) return -1;
122
+ return 0;
123
+ },
124
+ };
125
+
126
+ /**
127
+ * Returns a default bigint Comp instance that orders bigint numbers naturally.
128
+ */
129
+ export function bigIntComp(): Comp<bigint> {
130
+ return _bigIntComp;
131
+ }
132
+
133
+ const _defaultCollator = Intl.Collator('und');
134
+
135
+ const _stringComp: Comp<string> = {
136
+ isComparable(obj): obj is string {
137
+ return typeof obj === 'string';
138
+ },
139
+ compare: _defaultCollator.compare,
140
+ };
141
+
142
+ const _anyStringJSONComp: Comp<any> = {
143
+ isComparable(obj): obj is any {
144
+ return true;
145
+ },
146
+ compare(v1, v2) {
147
+ return _defaultCollator.compare(JSON.stringify(v1), JSON.stringify(v2));
148
+ },
149
+ };
150
+
151
+ /**
152
+ * Returns a Comp instance converts values to string with JSON.stringify, and orders the resulting string naturally.
153
+ */
154
+ export function anyStringJSONComp<T>(): Comp<T> {
155
+ return _anyStringJSONComp;
156
+ }
157
+
158
+ /**
159
+ * Returns a `Comp` instance that compares strings based on the string's `localeCompare` method.
160
+ * @param locales - (optional) a locale or list of locales
161
+ * @param options - (optional) see String.localeCompare for details
162
+ */
163
+ export function stringComp(
164
+ ...args: ConstructorParameters<typeof Intl.Collator>
165
+ ): Comp<string> {
166
+ if (args.length === 0) return _stringComp;
167
+
168
+ const collator = Intl.Collator(...args);
169
+
170
+ return {
171
+ isComparable(obj): obj is string {
172
+ return typeof obj === 'string';
173
+ },
174
+ compare: collator.compare,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Returns a `Comp` instance that compares strings in a case-insensitive way.
180
+ */
181
+ export function stringCaseInsensitiveComp(): Comp<string> {
182
+ return stringComp('und', { sensitivity: 'accent' });
183
+ }
184
+
185
+ const _stringCharCodeComp: Comp<string> = {
186
+ isComparable(obj: any): obj is any {
187
+ return typeof obj === 'string';
188
+ },
189
+ compare(v1: any, v2: any): number {
190
+ const len = Math.min(v1.length, v2.length);
191
+
192
+ let i = -1;
193
+
194
+ while (++i < len) {
195
+ const diff = v1.charCodeAt(i) - v2.charCodeAt(i);
196
+ if (diff !== 0) return diff;
197
+ }
198
+
199
+ return v1.length - v2.length;
200
+ },
201
+ };
202
+
203
+ /**
204
+ * Returns a string Comp instance that orders strings according to their indexed char codes.
205
+ */
206
+ export function stringCharCodeComp(): Comp<string> {
207
+ return _stringCharCodeComp;
208
+ }
209
+
210
+ const _anyToStringComp: Comp<any> = {
211
+ isComparable(obj: any): obj is any {
212
+ return true;
213
+ },
214
+ compare(v1: any, v2: any): number {
215
+ return _defaultCollator.compare(
216
+ Eq.convertAnyToString(v1),
217
+ Eq.convertAnyToString(v2)
218
+ );
219
+ },
220
+ };
221
+
222
+ /**
223
+ * Returns a any Comp instance that orders any according to their toString values.
224
+ */
225
+ export function anyToStringComp(): Comp<any> {
226
+ return _anyToStringComp;
227
+ }
228
+
229
+ /**
230
+ * Returns a Comp instance that orders objects with a `valueOf` method according to the given `valueComp` instance for the valueOf values.
231
+ * @param cls - the constructor of the values the Comp instance can compare
232
+ * @param valueComp - (optional) the Comp instance to use on the .valueOf values
233
+ */
234
+ export function createValueOfComp<T extends { valueOf(): V }, V>(
235
+ cls: {
236
+ new (): T;
237
+ },
238
+ valueComp: Comp<V> = anyShallowComp()
239
+ ): Comp<T> {
240
+ return {
241
+ isComparable(obj): obj is T {
242
+ return obj instanceof cls;
243
+ },
244
+ compare(v1, v2): number {
245
+ return valueComp.compare(v1.valueOf(), v2.valueOf());
246
+ },
247
+ };
248
+ }
249
+
250
+ const _DateComp: Comp<Date> = createValueOfComp(Date, _numberComp);
251
+
252
+ /**
253
+ * Returns a Date Comp instance that orders Dates according to their `.valueOf` value.
254
+ */
255
+ export function dateComp(): Comp<Date> {
256
+ return _DateComp;
257
+ }
258
+
259
+ function createIterableComp<T>(itemComp: Comp<T>): Comp<Iterable<T>> {
260
+ return {
261
+ isComparable(obj): obj is Iterable<T> {
262
+ // unfortunately we cannot check element compatability
263
+ return (
264
+ typeof obj === 'object' && obj !== null && Symbol.iterator in obj
265
+ );
266
+ },
267
+ compare(v1, v2): number {
268
+ const iter1 = v1[Symbol.iterator]();
269
+ const iter2 = v2[Symbol.iterator]();
270
+
271
+ while (true) {
272
+ const value1 = iter1.next();
273
+ const value2 = iter2.next();
274
+
275
+ if (value1.done) return value2.done ? 0 : -1;
276
+ if (value2.done) return 1;
277
+
278
+ const result = itemComp.compare(value1.value, value2.value);
279
+
280
+ if (result !== 0) return result;
281
+ }
282
+ },
283
+ };
284
+ }
285
+
286
+ const _iterableAnyComp: Comp<Iterable<any>> = createIterableComp(
287
+ Comp.defaultComp()
288
+ );
289
+
290
+ /**
291
+ * Returns a Comp instance for Iterable objects that orders the Iterables by comparing the elements with the given `itemComp` Comp instance.
292
+ * @param itemComp - (optional) the Comp instance to use to compare the Iterable's elements.
293
+ * @example
294
+ * ```ts
295
+ * const c = Comp.iterableComp();
296
+ * console.log(c.compare([1, 3, 2], [1, 3, 2]))
297
+ * // => 0
298
+ * console.log(c.compare([1, 2, 3, 4], [1, 3, 2]) < 0)
299
+ * // => true
300
+ * ```
301
+ */
302
+ export function iterableComp<T>(itemComp?: Comp<T>): Comp<Iterable<T>> {
303
+ if (undefined === itemComp) return _iterableAnyComp;
304
+
305
+ return createIterableComp(itemComp);
306
+ }
307
+
308
+ const _BooleanComp = createValueOfComp(Boolean, _booleanComp);
309
+ const _NumberComp = createValueOfComp(Number, _numberComp);
310
+ const _StringComp = createValueOfComp(String, _stringComp);
311
+
312
+ const _wrappedComps: Comp<unknown>[] = [
313
+ _BooleanComp,
314
+ _DateComp,
315
+ _NumberComp,
316
+ _StringComp,
317
+ ];
318
+
319
+ function tryWrappedComp(v1: any, v2: any): number | undefined {
320
+ let i = -1;
321
+ const len = _wrappedComps.length;
322
+
323
+ while (++i < len) {
324
+ const comp = _wrappedComps[i];
325
+
326
+ if (comp.isComparable(v1) && comp.isComparable(v2)) {
327
+ return comp.compare(v1, v2);
328
+ }
329
+ }
330
+
331
+ return undefined;
332
+ }
333
+
334
+ function createObjectComp(
335
+ keyComp: Comp<any> = anyFlatComp(),
336
+ valueComp: Comp<any> = defaultComp()
337
+ ): Comp<Record<any, any>> {
338
+ return {
339
+ isComparable(obj): obj is Record<any, any> {
340
+ return true;
341
+ },
342
+ compare(v1, v2): number {
343
+ const keys1 = Object.keys(v1);
344
+ const keys2 = Object.keys(v2);
345
+
346
+ if (keys1.length === 0) {
347
+ return keys2.length === 0 ? 0 : -1;
348
+ }
349
+ if (keys2.length === 0) {
350
+ return keys1.length === 0 ? 0 : 1;
351
+ }
352
+
353
+ keys1.sort(keyComp.compare);
354
+ keys2.sort(keyComp.compare);
355
+
356
+ const length = Math.min(keys1.length, keys2.length);
357
+
358
+ for (let index = 0; index < length; index++) {
359
+ const key1 = keys1[index];
360
+ const key2 = keys2[index];
361
+ const keyResult = keyComp.compare(key1, key2);
362
+
363
+ if (keyResult !== 0) return keyResult;
364
+
365
+ const value1 = v1[key1];
366
+ const value2 = v2[key2];
367
+
368
+ const valueResult = valueComp.compare(value1, value2);
369
+
370
+ if (valueResult !== 0) return valueResult;
371
+ }
372
+
373
+ const keyDiff = keys1.length - keys2.length;
374
+
375
+ return keyDiff;
376
+ },
377
+ };
378
+ }
379
+
380
+ const _objectAnyComp: Comp<Record<any, any>> = createObjectComp();
381
+
382
+ /**
383
+ * Returns a Comp instance for objects that orders the object keys according to the given `keyComp`, and then compares the corresponding
384
+ * values using the given `valueComp`. Objects are then compared as follows:<br/>
385
+ * starting with the smallest key of either object:<br/>
386
+ * - if only one of the objects has the key, the object with the key is considered to be larger than the other<br/>
387
+ * - if both objects have the key, the values are compared with `valueComp`. If the values are not equal, this result is returned.<br/>
388
+ *
389
+ * if the objects have the same keys with the same values, they are considered equal<br/>
390
+ * @param keyComp - (optional) the Comp instance used to order the object keys
391
+ * @param valueComp - (optional) the Comp instance used to order the object values
392
+ * @example
393
+ * ```ts
394
+ * const c = Comp.objectComp();
395
+ * console.log(c.compare({ a: 1 }, { a: 1 }))
396
+ * // => 0
397
+ * console.log(c.compare({ a: 1 }, { a: 2 }) < 0)
398
+ * // => true
399
+ * console.log(c.compare({ b: 5 }, { a: 2 }) < 0)
400
+ * // => true
401
+ * console.log(c.compare({ a: 1, b: 2 }, { b: 5 }) < 0)
402
+ * // => true
403
+ * console.log(c.compare({ a: 1, b: 2 }, { b: 2, a: 1 }))
404
+ * // => 0
405
+ * ```
406
+ */
407
+ export function objectComp(options?: {
408
+ keyComp?: Comp<any>;
409
+ valueComp?: Comp<any>;
410
+ }): Comp<Record<any, any>> {
411
+ if (undefined === options) return _objectAnyComp;
412
+
413
+ return createObjectComp(options.keyComp, options.valueComp);
414
+ }
415
+
416
+ function createAnyComp(mode: 'FLAT' | 'SHALLOW' | 'DEEP'): Comp<any> {
417
+ return {
418
+ isComparable(obj): obj is any {
419
+ return true;
420
+ },
421
+ compare(v1, v2): number {
422
+ if (Object.is(v1, v2)) return 0;
423
+
424
+ const type1 = typeof v1;
425
+ const type2 = typeof v2;
426
+
427
+ if (type1 !== type2) {
428
+ // we can only compare different types though strings
429
+ return _anyToStringComp.compare(v1, v2);
430
+ }
431
+
432
+ switch (type1) {
433
+ case 'bigint':
434
+ return _bigIntComp.compare(v1, v2);
435
+ case 'boolean':
436
+ return _booleanComp.compare(v1, v2);
437
+ case 'number':
438
+ return _numberComp.compare(v1, v2);
439
+ case 'string':
440
+ return _stringComp.compare(v1, v2);
441
+ case 'object': {
442
+ if (null === v1) {
443
+ if (null === v2) return 0;
444
+
445
+ return -1;
446
+ }
447
+ if (null === v2) {
448
+ return 1;
449
+ }
450
+
451
+ const wrappedComp = tryWrappedComp(v1, v2);
452
+
453
+ if (undefined !== wrappedComp) return wrappedComp;
454
+
455
+ if (mode !== 'FLAT') {
456
+ if (
457
+ _iterableAnyComp.isComparable(v1) &&
458
+ _iterableAnyComp.isComparable(v2)
459
+ ) {
460
+ if (mode === 'SHALLOW') {
461
+ return iterableComp(_anyFlatComp).compare(v1, v2);
462
+ }
463
+
464
+ return iterableComp(this).compare(v1, v2);
465
+ }
466
+
467
+ if (mode === 'SHALLOW') {
468
+ return createObjectComp(_anyFlatComp, _anyFlatComp).compare(
469
+ v1,
470
+ v2
471
+ );
472
+ }
473
+
474
+ return _objectAnyComp.compare(v1, v2);
475
+ }
476
+ }
477
+ }
478
+
479
+ return _anyToStringComp.compare(v1, v2);
480
+ },
481
+ };
482
+ }
483
+
484
+ /**
485
+ * Returns a Comp instance that compares any value using default comparison functions, but never recursively compares
486
+ * Iterables or objects. In those cases, it will use the stringComp instance.
487
+ * @example
488
+ * ```ts
489
+ * const c = Comp.anyFlatComp();
490
+ * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 }) < 0)
491
+ * // => true
492
+ * // First object is smaller because the objects are converted to a string with and then compares the resulting string.
493
+ * ```
494
+ */
495
+ export function anyFlatComp<T>(): Comp<T> {
496
+ return _anyFlatComp;
497
+ }
498
+
499
+ /**
500
+ * Returns a Comp instance that compares any value using default comparison functions. For Iterables and objects, their elements are compared
501
+ * only one level deep for performance and to avoid infinite recursion.
502
+ * @example
503
+ * ```ts
504
+ * const c = Comp.anyShallowComp();
505
+ * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 }))
506
+ * // => 0
507
+ * console.log(c.compare([{ a: 1, b: 1 }], [{ b: 1, a: 1 }]) < 0)
508
+ * // => true
509
+ * // First object is smaller because the objects are converted to a string and then compares the resulting string.
510
+ * ```
511
+ */
512
+ export function anyShallowComp<T>(): Comp<T> {
513
+ return _anyShallowComp;
514
+ }
515
+
516
+ /**
517
+ * Returns a Comp instance that compares any value using default comparison functions. For Iterables and objects, their elements are compared
518
+ * recursively.
519
+ * @note can become slow with large nested arrays and objects, and circular structures can cause infinite loops
520
+ * @example
521
+ * ```ts
522
+ * const c = Comp.anyDeepComp();
523
+ * console.log(c.compare({ a: 1, b: 1 }, { b: 1, a: 1 }))
524
+ * // => 0
525
+ * console.log(c.compare([{ a: 1, b: 1 }], [{ b: 1, a: 1 }]))
526
+ * // => 0
527
+ * ```
528
+ */
529
+ export function anyDeepComp<T>(): Comp<T> {
530
+ return _anyDeepComp;
531
+ }
532
+
533
+ /**
534
+ * Returns a Comp instance that extends the given `comp` instance with the capability to handle `undefined` values, where undefined is considered to be smaller
535
+ * than any other value, and equal to another undefined.
536
+ * @param comp - the Comp instance to wrap
537
+ * @example
538
+ * ```ts
539
+ * const c = Comp.withUndefined(Comp.numberComp())
540
+ * console.log(c.compare(undefined, 5) < 0)
541
+ * // => true
542
+ * console.log(c.compare(undefined, undefined))
543
+ * // => 0
544
+ * ```
545
+ */
546
+ export function withUndefined<T>(comp: Comp<T>): Comp<T | undefined> {
547
+ return {
548
+ isComparable(obj): obj is T | undefined {
549
+ return undefined === obj || comp.isComparable(obj);
550
+ },
551
+ compare(v1, v2): number {
552
+ if (undefined === v1) {
553
+ if (undefined === v2) return 0;
554
+ return -1;
555
+ }
556
+ if (undefined === v2) return 1;
557
+ return comp.compare(v1, v2);
558
+ },
559
+ };
560
+ }
561
+
562
+ /**
563
+ * Returns a Comp instance that extends the given `comp` instance with the capability to handle `null` values, where null is considered to be smaller
564
+ * than any other value, and equal to another null.
565
+ * @param comp - the Comp instance to wrap
566
+ * @example
567
+ * ```ts
568
+ * const c = Comp.withNull(Comp.numberComp())
569
+ * console.log(c.compare(null, 5) < 0)
570
+ * // => true
571
+ * console.log(c.compare(null, null))
572
+ * // => 0
573
+ * ```
574
+ */
575
+ export function withNull<T>(comp: Comp<T>): Comp<T | null> {
576
+ return {
577
+ isComparable(obj): obj is T | null {
578
+ return null === obj || comp.isComparable(obj);
579
+ },
580
+ compare(v1, v2): number {
581
+ if (null === v1) {
582
+ if (null === v2) return 0;
583
+ return -1;
584
+ }
585
+ if (null === v2) return 1;
586
+ return comp.compare(v1, v2);
587
+ },
588
+ };
589
+ }
590
+
591
+ /**
592
+ * Returns a Comp instance the reverses the order of the given `comp` instance.
593
+ * @param comp - the Comp instance to wrap
594
+ * @example
595
+ * ```ts
596
+ * const c = Comp.invert(Comp.numberComp())
597
+ * console.log(c.compare(3, 5) > 0)
598
+ * // => true
599
+ * console.log(c.compare(5, 5))
600
+ * // => 0
601
+ * ```
602
+ */
603
+ export function invert<T>(comp: Comp<T>): Comp<T> {
604
+ return {
605
+ compare(v1, v2): number {
606
+ return comp.compare(v2, v1);
607
+ },
608
+ isComparable: comp.isComparable,
609
+ };
610
+ }
611
+
612
+ /**
613
+ * Returns an `Eq` equality instance thet will return true when the given `comp` comparable instance returns 0.
614
+ * @param comp - the `Comp` comparable instance to convert
615
+ * @example
616
+ * ```ts
617
+ * const eq = Comp.toEq(Comp.objectComp())
618
+ * console.log(eq({ a: 1, b: 2 }, { b: 2, a: 1 }))
619
+ * // => true
620
+ * ```
621
+ */
622
+ export function toEq<T>(comp: Comp<T>): Eq<T> {
623
+ return (v1: T, v2: T): boolean => comp.compare(v1, v2) === 0;
624
+ }
625
+ }