Skip to content

Commit 6c8af05

Browse files
committed
Added Many-specific utilities.
1 parent f7e542a commit 6c8af05

File tree

4 files changed

+249
-3
lines changed

4 files changed

+249
-3
lines changed

src/defs.d.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface Many<T = any> {
6767
* @param o object to test
6868
* @returns `true` if `o` is a `Many`
6969
*/
70-
export declare function isMany(o: object): o is Many;
70+
export declare function isMany(o: unknown): o is Many;
7171
/**
7272
* Creates a `Many`
7373
* @param values the wrapped values
@@ -163,6 +163,53 @@ export declare function clearFunctionList<
163163
O = unknown
164164
>(o: FunctionList<T, I, O>): (value: I, ...rest: any[]) => O;
165165

166+
/**
167+
* Convert a value to `Many`.
168+
* @param value the value to convert
169+
* @returns a `Many` containing the value
170+
* @remarks `Many` is used to return multiple values from a regular (non-generator) function.
171+
*/
172+
export declare function toMany<T>(value: readonly Many<T>): Many<T>;
173+
export declare function toMany(value: typeof none): Many<never>;
174+
export declare function toMany<T>(value: readonly T): Many<T>;
175+
/**
176+
* Normalize a value by unbundling it if it is a `Many`.
177+
* @param value the value to normalize
178+
* @returns the normalized value
179+
*/
180+
export declare function normalizeMany(value: readonly unknown): unknown;
181+
/**
182+
* Combine two values into a `Many`.
183+
* @param a the first value
184+
* @param b the second value
185+
* @returns a `Many` containing both values
186+
*/
187+
export declare function combineMany(a: typeof none, b: typeof none): Many<never>;
188+
export declare function combineMany<T>(a: typeof none, b: readonly Many<T>): Many<T>;
189+
export declare function combineMany<T>(a: readonly Many<T>, b: typeof none): Many<T>;
190+
export declare function combineMany<T>(a: typeof none, b: readonly T): Many<T>;
191+
export declare function combineMany<T>(a: readonly T, b: typeof none): Many<T>;
192+
export declare function combineMany<T, U>(a: readonly Many<T>, b: readonly Many<U>): Many<T | U>;
193+
export declare function combineMany<T, U>(a: readonly T, b: readonly Many<U>): Many<T | U>;
194+
export declare function combineMany<T, U>(a: readonly Many<T>, b: readonly U): Many<T | U>;
195+
export declare function combineMany<T, U>(a: readonly T, b: readonly U): Many<T | U>;
196+
/**
197+
* Combine two values into a `Many` mutably.
198+
* @param a the first value
199+
* @param b the second value
200+
* @returns a `Many` containing both values
201+
* @remarks if `a` or `b` are `Many`, they can be modified in-place
202+
*/
203+
export declare function combineManyMut(a: typeof none, b: typeof none): Many<never>;
204+
export declare function combineManyMut<T>(a: typeof none, b: readonly Many<T>): Many<T>;
205+
export declare function combineManyMut<T>(a: readonly Many<T>, b: typeof none): Many<T>;
206+
export declare function combineManyMut<T>(a: typeof none, b: readonly T): Many<T>;
207+
export declare function combineManyMut<T>(a: readonly T, b: typeof none): Many<T>;
208+
export declare function combineManyMut<T, U>(a: readonly Many<T>, b: readonly Many<U>): Many<T | U>;
209+
export declare function combineManyMut<T, U>(a: readonly T, b: readonly Many<U>): Many<T | U>;
210+
export declare function combineManyMut<T, U>(a: readonly Many<T>, b: readonly U): Many<T | U>;
211+
export declare function combineManyMut<T, U>(a: readonly T, b: readonly U): Many<T | U>;
212+
166213
// generic utilities: unpacking types
167214

168215
/**

src/defs.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,55 @@ const setFunctionList = (o, fns) => {
3232
o.fList = fns;
3333
o[fListSymbol] = 1;
3434
return o;
35-
}
35+
};
3636

3737
const clearFunctionList = o => {
3838
delete o.fList;
3939
delete o[fListSymbol];
4040
return o;
41-
}
41+
};
4242

4343
class Stop extends Error {}
4444

45+
const toMany = value =>
46+
value === none ? many([]) : value && value[manySymbol] === 1 ? value : many([value]);
47+
48+
const normalizeMany = o => {
49+
if (o?.[manySymbol] === 1) {
50+
switch (o.values.length) {
51+
case 0:
52+
return none;
53+
case 1:
54+
return o.values[0];
55+
}
56+
}
57+
return o;
58+
};
59+
60+
const combineMany = (a, b) => {
61+
const values = a === none ? [] : a?.[manySymbol] === 1 ? a.values.slice() : [a];
62+
if (b === none) {
63+
// do nothing
64+
} else if (b?.[manySymbol] === 1) {
65+
values.push(...b.values);
66+
} else {
67+
values.push(b);
68+
}
69+
return many(values);
70+
};
71+
72+
const combineManyMut = (a, b) => {
73+
const values = a === none ? [] : a?.[manySymbol] === 1 ? a.values : [a];
74+
if (b === none) {
75+
// do nothing
76+
} else if (b?.[manySymbol] === 1) {
77+
values.push(...b.values);
78+
} else {
79+
values.push(b);
80+
}
81+
return many(values);
82+
};
83+
4584
// old aliases
4685
const final = finalValue;
4786

@@ -70,3 +109,8 @@ module.exports.isFunctionList = isFunctionList;
70109
module.exports.getFunctionList = getFunctionList;
71110
module.exports.setFunctionList = setFunctionList;
72111
module.exports.clearFunctionList = clearFunctionList;
112+
113+
module.exports.toMany = toMany;
114+
module.exports.normalizeMany = normalizeMany;
115+
module.exports.combineMany = combineMany;
116+
module.exports.combineManyMut = combineManyMut;

tests/test-defs.mjs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
'use strict';
2+
3+
import test from 'tape-six';
4+
5+
import {
6+
getManyValues,
7+
many,
8+
none,
9+
toMany,
10+
normalizeMany,
11+
combineMany,
12+
combineManyMut
13+
} from '../src/defs.js';
14+
15+
test('defs: toMany()', t => {
16+
t.deepEqual(getManyValues(toMany(none)), []);
17+
t.deepEqual(getManyValues(toMany(1)), [1]);
18+
t.deepEqual(getManyValues(toMany(many([]))), []);
19+
t.deepEqual(getManyValues(toMany(many([1, 2, 3]))), [1, 2, 3]);
20+
});
21+
22+
test('defs: normalizeMany()', t => {
23+
t.deepEqual(normalizeMany(none), none);
24+
t.deepEqual(normalizeMany(1), 1);
25+
t.deepEqual(normalizeMany(many([])), none);
26+
t.deepEqual(normalizeMany(many([1])), 1);
27+
t.deepEqual(getManyValues(normalizeMany(many([1, 2, 3]))), [1, 2, 3]);
28+
});
29+
30+
test('defs: combineMany()', t => {
31+
t.deepEqual(getManyValues(combineMany(none, none)), []);
32+
t.deepEqual(getManyValues(combineMany(none, 2)), [2]);
33+
t.deepEqual(getManyValues(combineMany(1, none)), [1]);
34+
t.deepEqual(getManyValues(combineMany(none, many([]))), []);
35+
t.deepEqual(getManyValues(combineMany(1, many([]))), [1]);
36+
t.deepEqual(getManyValues(combineMany(none, many([1, 2, 3]))), [1, 2, 3]);
37+
t.deepEqual(getManyValues(combineMany(0, many([1, 2, 3]))), [0, 1, 2, 3]);
38+
t.deepEqual(getManyValues(combineMany(many([]), none)), []);
39+
t.deepEqual(getManyValues(combineMany(many([]), 1)), [1]);
40+
t.deepEqual(getManyValues(combineMany(many([1]), 2)), [1, 2]);
41+
t.deepEqual(getManyValues(combineMany(many([1, 2, 3]), many([]))), [1, 2, 3]);
42+
t.deepEqual(getManyValues(combineMany(many([1, 2, 3]), many([4, 5, 6]))), [1, 2, 3, 4, 5, 6]);
43+
});
44+
45+
test('defs: combineMany() - immutability', t => {
46+
const a = many([1, 2, 3]),
47+
b = many([4, 5, 6]),
48+
c = combineMany(a, b);
49+
t.deepEqual(getManyValues(a), [1, 2, 3]);
50+
t.deepEqual(getManyValues(b), [4, 5, 6]);
51+
t.deepEqual(getManyValues(c), [1, 2, 3, 4, 5, 6]);
52+
});
53+
54+
test('defs: combineManyMut()', t => {
55+
t.deepEqual(getManyValues(combineManyMut(none, none)), []);
56+
t.deepEqual(getManyValues(combineManyMut(none, 2)), [2]);
57+
t.deepEqual(getManyValues(combineManyMut(1, none)), [1]);
58+
t.deepEqual(getManyValues(combineManyMut(none, many([]))), []);
59+
t.deepEqual(getManyValues(combineManyMut(1, many([]))), [1]);
60+
t.deepEqual(getManyValues(combineManyMut(none, many([1, 2, 3]))), [1, 2, 3]);
61+
t.deepEqual(getManyValues(combineManyMut(0, many([1, 2, 3]))), [0, 1, 2, 3]);
62+
t.deepEqual(getManyValues(combineManyMut(many([]), none)), []);
63+
t.deepEqual(getManyValues(combineManyMut(many([]), 1)), [1]);
64+
t.deepEqual(getManyValues(combineManyMut(many([1]), 2)), [1, 2]);
65+
t.deepEqual(getManyValues(combineManyMut(many([1, 2, 3]), many([]))), [1, 2, 3]);
66+
t.deepEqual(getManyValues(combineManyMut(many([1, 2, 3]), many([4, 5, 6]))), [1, 2, 3, 4, 5, 6]);
67+
});
68+
69+
test('defs: combineManyMut() - mutability', t => {
70+
const a = many([1, 2, 3]),
71+
b = many([4, 5, 6]),
72+
c = combineManyMut(a, b);
73+
t.deepEqual(getManyValues(a), [1, 2, 3, 4, 5, 6]);
74+
t.deepEqual(getManyValues(b), [4, 5, 6]);
75+
t.deepEqual(getManyValues(c), [1, 2, 3, 4, 5, 6]);
76+
});

ts-test/defs.mts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import {
2+
none,
3+
many,
4+
isMany,
5+
toMany,
6+
normalizeMany,
7+
getManyValues,
8+
combineMany,
9+
combineManyMut
10+
} from 'stream-chain/defs.js';
11+
12+
{
13+
const a = many([]),
14+
b = many([1]),
15+
c = many([1, 2]);
16+
17+
console.assert(getManyValues(a).length === 0);
18+
console.assert(getManyValues(b).length === 1);
19+
console.assert(getManyValues(c).length === 2);
20+
21+
console.assert(normalizeMany(a) === none);
22+
console.assert(normalizeMany(b) === 1);
23+
24+
const x = normalizeMany(c);
25+
console.assert(isMany(x));
26+
if (isMany(x)) console.assert(getManyValues(x).length === 2);
27+
}
28+
29+
{
30+
const a = toMany(none),
31+
b = toMany(1),
32+
c = toMany(many([])),
33+
d = toMany(many([1, 2, 3]));
34+
35+
console.assert(getManyValues(a).length === 0);
36+
console.assert(getManyValues(b).length === 1);
37+
console.assert(getManyValues(c).length === 0);
38+
console.assert(getManyValues(d).length === 3);
39+
}
40+
41+
{
42+
const a = combineMany(none, none),
43+
b = combineMany(none, 1),
44+
c = combineMany(1, none),
45+
d = combineMany(1, '2'),
46+
e = combineMany(many([]), many([])),
47+
f = combineMany(many([]), many([1, 2, 3])),
48+
g = combineMany(many([1, 2, 3]), many([])),
49+
h = combineMany(many([1, '2', 3]), many([4, 5, 6]));
50+
51+
console.assert(getManyValues(a).length === 0);
52+
console.assert(getManyValues(b).length === 1);
53+
console.assert(getManyValues(c).length === 1);
54+
console.assert(getManyValues(d).length === 2);
55+
console.assert(getManyValues(e).length === 0);
56+
console.assert(getManyValues(f).length === 3);
57+
console.assert(getManyValues(g).length === 3);
58+
console.assert(getManyValues(h).length === 6);
59+
}
60+
61+
{
62+
const a = combineManyMut(none, none),
63+
b = combineManyMut(none, 1),
64+
c = combineManyMut(1, none),
65+
d = combineManyMut(1, '2'),
66+
e = combineManyMut(many([]), many([])),
67+
f = combineManyMut(many([]), many([1, 2, 3])),
68+
g = combineManyMut(many([1, 2, 3]), many([])),
69+
h = combineManyMut(many([1, '2', 3]), many([4, 5, 6]));
70+
71+
console.assert(getManyValues(a).length === 0);
72+
console.assert(getManyValues(b).length === 1);
73+
console.assert(getManyValues(c).length === 1);
74+
console.assert(getManyValues(d).length === 2);
75+
console.assert(getManyValues(e).length === 0);
76+
console.assert(getManyValues(f).length === 3);
77+
console.assert(getManyValues(g).length === 3);
78+
console.assert(getManyValues(h).length === 6);
79+
}

0 commit comments

Comments
 (0)