Arrays
GenSX includes an array primitive that lets you operate over arrays of components with the same standard JavaScript APIs you’re used to including map
, filter
, flatMap
, and reduce
. This allows you to chain together array operations over lists of components for scenarios like:
- Evaluating the usefulness of each chunk returned by your vector database
- Translating a single document into multiple languages
- Analyzing key themes in a set of customer reviews
When you use gensx.array
, GenSX takes care of executing components and producing outputs as needed so that you can focus on the logic of your workflow.
Example
Here’s a practical example of using gensx.array
. The Research
component below receives a prompt and a list of queries, and then returns a list of summaries from relevant research papers.
const Research = gensx.Component<ResearchProps, ArxivSummary[]>(
"Research",
async ({ queries: string[], prompt: string }) => {
return await gensx
.array<string>(queries)
.flatMap<ArxivEntry>((query) => <ArxivSearch query={query} maxResults={3} />)
.filter((document) => (
<GradeDocument prompt={prompt} document={document} />
))
.map<ArxivSummary>((document) => (
<FetchAndSummarize document={document} prompt={prompt} />
))
.toArray();
},
);
Walking through this code step by step, it:
- Instantiates a
GsxArray<string>
from thequeries
- Uses
flatMap
to call theArxivSearch
component for each query and flatten the output into a single array of documents - Calls
filter
to filter out any documents that an LLM judge deems irrelevant.GradeDocument
returns a boolean - Chains another
map
operation to call theFetchAndSummarize
component for each document - Calls
toArray()
to convert theGsxArray<ArxivSummary>
into aArxivSummary[]
You can find the full code for this in the Deep Research example .
Working with arrays
Now that you’ve seen an example showing a lot of the functionality of gensx.array
, let’s explore each of these operations in more detail.
Creating an array
To access the array operations, you first need to create a GsxArray
. You can create an array from raw values:
import * as gensx from "@gensx/core";
// Create from raw values
const numbers = gensx.array<number>([1, 2, 3]);
You can also create arrays from components:
const NumberWrapper = gensx.Component<{ n: number }, number>(
"NumberWrapper",
({ n }) => n,
);
// Create from components
const wrappedNumbers = gensx.array<NumberWrapper>([
<NumberWrapper n={1} />,
<NumberWrapper n={2} />,
<NumberWrapper n={3} />,
]);
To convert a GsxArray
back to a plain array, you simply call the toArray()
method which produces an array of resolved component outputs.
Map and FlatMap
gensx.array
supports both map
and flatMap
operations. Both of these operations behave exactly as you’d expect if you’re familiar with array.map
and array.flatMap
in JavaScript.
Map
Map transforms each element in an array using a component:
const numbers = gensx.array([1, 2, 3]);
const doubled = await numbers
.map<number>((n) => <NumberDoubler value={n} />)
.toArray();
// Result: [2, 4, 6]
FlatMap
Similarly, flatMap invokes a component for each element in the array and flattens the result.
For example, the code below retrieves an array of search results for each query and flattens the results into a single array:
const queries = gensx.array(["chain of thought", "reasoning models"]);
const documents = await queries
.flatMap<SearchResult>((query) => (
<WebSearch query={query} /> // returns a list of documents
))
.toArray();
Filter
Filters allow you to filter out elements that don’t match a given condition. Inside the filter predicate, you can pass either:
- A component that returns a boolean
- A plain boolean expression
Filtering with a component
When you filter with a component, the component needs to result in a boolean value.
// Define a component that returns a boolean
const EvenNumberFilter = gensx.Component<{ value: number }, boolean>(
"EvenNumberFilter",
({ value }) => {
return value % 2 === 0;
},
);
// Use a component that returns a boolean to filter
const evenNumbers = await gensx
.array<number>([10, 11, 12, 13, 14])
.filter((n) => <EvenNumberFilter value={n} />)
.toArray();
You can also use the component’s child function to convert the output of a component into a boolean:
const evenNumbers = await gensx
.array<number>([10, 11, 12, 13, 14])
.filter((n) => (
<EvenOrOdd value={n}>
{({ result: string }) => result.toLowerCase() === "even"}
</EvenOrOdd>
))
.toArray();
Filter using a plain boolean expression
When you filter with a boolean expression, you use the filter method just like you would with JavaScript arrays:
const evenNumbers = await gensx
.array<number>([10, 11, 12, 13, 14])
.filter((n) => n % 2 === 0)
.toArray();
Filter using index and array parameters
Filter expressions can also access the current index and the entire array. Here’s a basic example of using this to remove duplicates from an array:
const uniqueNumbers = await gensx
.array<number>([1, 2, 2, 3, 3, 3, 4])
.filter((num, index, array) => array.indexOf(num) === index)
.toArray();
// Result: [1, 2, 3, 4]
Reduce
The reduce operation allows you to process all of the items in an array to produce a single value.
For example, if you wanted to translate a markdown document section by section and combine the results into a single document, it would look something like this:
const markdownContent = "<some markdown content>";
const translatedContent = await gensx
.array<string>(markdownContent.split(/(?=^#{1,2} )/m))
.map<string>((value) => <TranslateSection value={value} />)
.reduce<string>(
(acc, value) => <CombineSections acc={acc} value={value} />,
"",
);
To break down this example a bit more:
acc
is the accumulator, which starts as the initial value, in this case an empty stringvalue
is the current section of the markdown contentCombineSections
is a component that does the combining given both the accumulator and the current value
How array operations work
gensx.array
is a convenience wrapper around gensx.execute
function and the corresponding JavaScript array methods.
To illustrate this, take the following example:
const numbers = [1, 2, 3];
const result = await gensx
.array<number>(numbers)
.map<number>((n) => <NumberDoubler value={n} />)
.filter((n) => <EvenNumberFilter value={n} />)
.toArray();
That same code can be written using gensx.execute
like this:
const numbers = [1, 2, 3];
const doubledNumbers = await gensx.execute<number[]>(
numbers.map((n) => <NumberDoubler value={n} />),
);
const result = await gensx.execute<number[]>(
doubledNumbers.filter((n) => <EvenNumberFilter value={n} />),
);
When using gensx.array
, each operation in the chain will execute in sequence so if you have gensx.array().map().map()
, all of the components in the first map
will execute in parallel and will complete before the second map
starts.