Skip to content

Commit db24f95

Browse files
committed
feat: improve weave helper function
1 parent 22a8a7d commit db24f95

File tree

1 file changed

+76
-7
lines changed

1 file changed

+76
-7
lines changed

src/helpers.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,93 @@
11
export interface WeaveOptions<TInput> {
2+
/**
3+
* The amount of items to generate
4+
* @default 10
5+
*/
26
amount: number;
7+
8+
/**
9+
* A function that will be called for each item to be generated
10+
* @param {TInput} input - The input object
11+
* @param {number} index - The index of the item to be generated
12+
* @returns {TInput} The generated item
13+
*/
314
customizer: (input: TInput, index: number) => TInput;
15+
16+
/**
17+
* A list of fields that will be used to generate random values
18+
* @type {{ [K in keyof Partial<TInput>]: Array<TInput[K]> }}
19+
*/
20+
fields?: {
21+
[K in keyof Partial<TInput>]: Array<TInput[K]>;
22+
};
423
}
524

625
const DEFAULT_WEAVE_OPTIONS: WeaveOptions<any> = {
726
amount: 10,
827
customizer: (input) => input,
28+
fields: {},
929
};
1030

11-
export function weave<TInput>(input: TInput | TInput[], opts?: WeaveOptions<TInput>): TInput[] {
12-
const { amount, customizer } = opts ?? DEFAULT_WEAVE_OPTIONS;
31+
export type EnsureValidWeaveInput<TInput> =
32+
TInput extends any[]
33+
? TInput extends (infer TArrayInput)[]
34+
? TArrayInput extends Record<string, unknown>
35+
? TArrayInput[]
36+
: never
37+
: never
38+
: TInput extends Record<string, unknown>
39+
? TInput
40+
: never;
41+
42+
type RemoveArrayType<TInput> = TInput extends Array<infer TArrayInput> ? TArrayInput : TInput;
43+
44+
/**
45+
* Weaves new objects into an array based on a template from the input array.
46+
*
47+
* @template TInput The type of the input array elements.
48+
* @param {EnsureValidWeaveInput<TInput>} input An array of objects to use as templates.
49+
* @param {WeaveOptions<RemoveArrayType<TInput>>} [opts] Optional configuration for the weaving process.
50+
* @param {number} [opts.amount] The number of new objects to weave into the array. Must be a positive number.
51+
* @param {WeaveFields<RemoveArrayType<TInput>>} [opts.fields] An object specifying fields to randomize in the new objects. Each field should be an array of possible values.
52+
* @param {WeaveCustomizer<RemoveArrayType<TInput>>} [opts.customizer] A function to customize each new object after field randomization.
53+
* @returns {RemoveArrayType<TInput>[]} A new array containing the original input array elements plus the woven objects.
54+
* @throws {Error} If the input is not a non-empty array, or if the amount is not a positive number.
55+
*/
56+
export function weave<TInput>(input: EnsureValidWeaveInput<TInput>, opts?: WeaveOptions<RemoveArrayType<TInput>>): RemoveArrayType<TInput>[] {
57+
const { amount, customizer, fields } = opts ?? DEFAULT_WEAVE_OPTIONS;
58+
59+
if (!Array.isArray(input) || input.length === 0) {
60+
throw new Error("Input must be a non-empty array of objects");
61+
}
62+
63+
if (typeof amount !== "number" || amount <= 0) {
64+
throw new Error("Amount must be a positive number");
65+
}
1366

14-
// convert single input to array
15-
const inputs = Array.isArray(input) ? input : [input];
16-
const result: TInput[] = [];
67+
const result: RemoveArrayType<TInput>[] = [
68+
...input,
69+
];
1770

1871
for (let i = 0; i < amount; i++) {
19-
for (const item of inputs) {
20-
result.push(customizer(item, i));
72+
const templateIndex = Math.floor(Math.random() * input.length) ?? 0;
73+
let template = { ...input[templateIndex] };
74+
75+
// apply random values from fields if provided
76+
if (fields && typeof fields === "object") {
77+
(Object.keys(fields) as Array<keyof RemoveArrayType<TInput>>).forEach((field) => {
78+
if (field in template && Array.isArray(fields[field]) && fields[field]!.length > 0) {
79+
const randomValueIndex = Math.floor(Math.random() * fields[field]!.length);
80+
template[field] = fields[field]![randomValueIndex];
81+
}
82+
});
2183
}
84+
85+
// apply customizer if provided
86+
if (customizer && typeof customizer === "function") {
87+
template = customizer(template, i);
88+
}
89+
90+
result.push(template);
2291
}
2392

2493
return result;

0 commit comments

Comments
 (0)