Arrays
LittleHorse provides a native ARRAY variable type that gives you strongly-typed, ordered lists with per-element type enforcement.
Use ARRAY when you want compile-time and runtime guarantees that every element in your list conforms to a specific type.
In LittleHorse 1.1, we added support for Arrays in the Java and .NET SDKs. Support for other languages will be added in future releases.
Supported Element Types
An ARRAY can hold elements of many TypeDefinition:
- Primitives:
INT,DOUBLE,STR,BOOL,BYTES, etc... - Complex types:
STRUCT(backed by a registeredStructDef) - Nested arrays:
ARRAY(arrays of arrays)
ARRAY variables cannot contain JSON_ARR or JSON_OBJ elements, since those types do not have a defined schema for validation, and would defeat the purpose of using a typed array.
Input Validation
The LittleHorse server validates array inputs at ingress (e.g., when running a workflow or returning from a task). If an element does not match the Array's declared type, the server rejects the request with INVALID_ARGUMENT.
In Practice
To use an ARRAY variable in LittleHorse:
- Declare an
ARRAYvariable in yourWfSpecusingdeclareArray(). - Produce and consume arrays in your task workers, marking them as distinct
ARRAYs instead ofJSON_ARRusing the@LHType(isLHArray = true)annotation. - Use mutation operations to manipulate array contents.
Declaring an ARRAY Variable
Use the declareArray method on WorkflowThread to create a typed array variable. You specify the element type as a class.
- Java
- C#
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public void wfLogic(WorkflowThread wf) {
// Declare an array of Long elements
WfRunVariable longArray = wf.declareArray("my-numbers", Long.class);
// Declare an array of String elements
WfRunVariable stringArray = wf.declareArray("my-strings", String.class);
}
private static Workflow GetWorkflow()
{
void Entrypoint(WorkflowThread wf)
{
// Declare an array of Long elements
WfRunVariable longArray = wf.DeclareArray("my-numbers", typeof(long));
// Declare an array of String elements
WfRunVariable stringArray = wf.DeclareArray("my-strings", typeof(string));
}
return new Workflow("example-arrays", Entrypoint);
}
Task Workers with Native Arrays
Task workers can produce and consume native arrays using the @LHType(isLHArray = true) annotation. Without the annotation, Java arrays would default to JSON_ARR serialization. This is done for backwards compatibility purposes, since old clients automatically treated Java arrays as JSON arrays.
Producing an Array
Annotate the task method with @LHType(isLHArray = true) and return a Java array:
- Java
- C#
import io.littlehorse.sdk.worker.LHTaskMethod;
import io.littlehorse.sdk.worker.LHType;
public class ArrayWorker {
@LHTaskMethod("produce-array")
@LHType(isLHArray = true)
public Long[] produceArray() {
return new Long[] {1L, 2L, 3L};
}
}
public class ArrayWorker
{
[LHTaskMethod("produce-array")]
[LHType(masked: false, isLHArray: true)]
public Task<long[]> ProduceArray()
{
return Task.FromResult(new long[] { 1L, 2L, 3L });
}
}
Consuming an Array
Annotate a method parameter with @LHType(isLHArray = true) and accept a Java array:
- Java
- C#
import io.littlehorse.sdk.worker.LHTaskMethod;
import io.littlehorse.sdk.worker.LHType;
public class ArrayWorker {
@LHTaskMethod("consume-array")
public String consumeArray(@LHType(isLHArray = true) Long[] arr) {
return "Received " + arr.length + " elements";
}
}
public class ArrayWorker
{
[LHTaskMethod("consume-array")]
public Task<string> ConsumeArray([LHType(masked: false, isLHArray: true)] long[] arr)
{
return Task.FromResult($"Received {arr.Length} elements");
}
}
Arrays in Struct Fields
For Struct properties that are Arrays, you only need to use the Array's type as the field type. No special annotation is needed on the field itself, since StructDef schemas only support LH Arrays and do not have JSON array support.
- Java
- C#
import io.littlehorse.sdk.worker.LHStructDef;
import io.littlehorse.sdk.worker.LHStructField;
@LHStructDef("shopping-cart")
public class ShoppingCart {
private Long[] itemIds;
public Long[] getItemIds() {
return itemIds;
}
public void setItemIds(Long[] itemIds) {
this.itemIds = itemIds;
}
}
[LHStructDef("shopping-cart")]
public class ShoppingCart
{
public long[] ItemIds { get; set; }
public ShoppingCart() { }
public ShoppingCart(long[] itemIds)
{
ItemIds = itemIds;
}
}
Mutation Operations
The ARRAY type supports the following mutation operations:
| Operation | Description |
|---|---|
ASSIGN | Assign an array value to the variable |
EXTEND | Append a single element to the array (type-checked against the element type) |
REMOVE_IF_PRESENT | Remove all occurrences of a value from the array |
REMOVE_INDEX | Remove the element at a specific index |
SIZE_OF | Get the number of elements in the array |
- Java
import io.littlehorse.sdk.wfsdk.NodeOutput;
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public void wfLogic(WorkflowThread wf) {
WfRunVariable arrVar = wf.declareArray("my-array", Long.class);
WfRunVariable arrSize = wf.declareInt("array-size");
// Assign the result of a task to the array
NodeOutput produced = wf.execute("produce-array");
arrVar.assign(produced);
// Append a single element
arrVar.assign(arrVar.extend(4L));
// Remove all occurrences of a value
arrVar.assign(arrVar.removeIfPresent(2L));
// Remove the element at index 1
arrVar.assign(arrVar.removeIndex(1));
// Get the size of the array
arrSize.assign(arrVar.size());
}
Accessing Array Elements
You can use index-based access to read individual elements from an ARRAY variable using the .get() method with a numeric index:
- Java
public void wfLogic(WorkflowThread wf) {
WfRunVariable arrVar = wf.declareArray("my-array", Long.class);
// Access the element at index 0
wf.execute("process-item", arrVar.get(0));
// Access the element at index 2
wf.execute("process-item", arrVar.get(2));
}
Conditionals with IN and NOT_IN
The doesContain and doesNotContain methods work with native ARRAY variables:
- Java
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public void wfLogic(WorkflowThread wf) {
WfRunVariable arrVar = wf.declareArray("my-array", Long.class);
WfRunVariable valueToCheck = wf.declareInt("value-to-check");
wf.doIfElse(
arrVar.doesContain(valueToCheck),
ifBody -> {
ifBody.execute("handle-found", valueToCheck);
},
elseBody -> {
elseBody.execute("handle-not-found", valueToCheck);
}
);
}
Spawning Threads per Element
The spawnThreadForEach method works with native ARRAY variables, spawning one child thread per element in the array. Each child thread receives the corresponding element as input.
- Java
import io.littlehorse.sdk.wfsdk.SpawnedThreads;
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public void wfLogic(WorkflowThread wf) {
WfRunVariable arrVar = wf.declareArray("my-array", Long.class);
SpawnedThreads threads = wf.spawnThreadForEach(
arrVar,
"process-element",
this::processElementThread,
null
);
wf.waitForThreads(threads);
}
public void processElementThread(WorkflowThread wf) {
WfRunVariable input = wf.declareInt("INPUT").required();
wf.execute("process-item", input);
}
Full Example
Complete working examples for the Java and .NET SDKs are available in the [LittleHorse examples repository]:
- Java: https://github.com/littlehorse-enterprises/littlehorse/tree/master/examples/java/arrays.
- .NET: https://github.com/littlehorse-enterprises/littlehorse/tree/master/examples/dotnet/ArraysExample.
Further Resources
- Variables Concepts — overview of all variable types in LittleHorse.
- Mutating Variables — guide to variable mutation operations.
- StructDefs and Structs — another complex type that can be used as an
ARRAYelement type. - Threads — learn about
spawnThreadForEachand other threading concepts. - Java Arrays Example — full working example on GitHub.