Skip to Content
ConceptsUsing array operations

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:

  1. Instantiates a GsxArray<string> from the queries
  2. Uses flatMap to call the ArxivSearch component for each query and flatten the output into a single array of documents
  3. Calls filter to filter out any documents that an LLM judge deems irrelevant. GradeDocument returns a boolean
  4. Chains another map operation to call the FetchAndSummarize component for each document
  5. Calls toArray() to convert the GsxArray<ArxivSummary> into a ArxivSummary[]

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 string
  • value is the current section of the markdown content
  • CombineSections 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.

Last updated on