Select Many
Flatten arrays into individual items for processing
The Select Many node expands an array into individual items, enabling per-item processing of collections. Use it to iterate over attendees, action items, or any list that needs individual handling.
Overview
| Property | Value |
|---|---|
| Category | Control |
| Node ID | ds.selectMany.perItem.in1.success1.error0 |
| Input Ports | 1 |
| Success Outputs | 1 |
| Error Outputs | 0 |
| Execution Mode | Per-item |
Configuration
| Parameter | Type | Required | Description |
|---|---|---|---|
items | CEL Expression | Yes | Array to expand |
label | String | No | Label for each item in output |
Items Parameter
A CEL expression that evaluates to an array:
json.meeting.attendees
json.actionItems
json.callRecording.keyStatements
Label Parameter
Optional label for accessing items downstream. If not set, items are at the root of json.
How It Works
Input: { "items": [A, B, C] }
│
▼
[Select Many]
│
├──▶ Item: A
├──▶ Item: B
└──▶ Item: C
The Select Many node:
- Receives input with an array
- Extracts the specified array
- Emits each element as a separate output
- Downstream nodes execute once per element
Input Schema
Expects data containing the array specified in items:
{
"meeting": {
"attendees": [
{ "name": "John", "email": "john@example.com" },
{ "name": "Jane", "email": "jane@example.com" }
]
}
}
Output Schema
Without Label
Each item becomes the entire json:
// First output
{ "name": "John", "email": "john@example.com" }
// Second output
{ "name": "Jane", "email": "jane@example.com" }
With Label
Each item is nested under the label:
Configuration: label = "attendee"
// First output
{ "attendee": { "name": "John", "email": "john@example.com" } }
// Second output
{ "attendee": { "name": "Jane", "email": "jane@example.com" } }
Examples
Basic Example: Email Each Attendee
Send individual emails to each meeting attendee.
Workflow:
[Load Meeting]
│
▼
[Select Many: json.meeting.attendees]
│
▼
[Email Send] ← Runs once per attendee
Configuration:
- Items:
json.meeting.attendees - Label:
attendee
Email Send Configuration:
- To:
json.attendee.email - Subject:
Thank you for attending!
Example: Create Tasks from Action Items
Create individual tasks for each extracted action item.
Workflow:
[AI: Extract action items]
│
▼
[Select Many: json.actionItems]
│
▼
[Create HubSpot Task]
Configuration:
- Items:
json.actionItems - Label:
item
Task Configuration:
- Title:
{{ json.item.task }} - Description:
Assigned to: {{ json.item.owner | default: "Unassigned" }}
Example: Process Key Statements
Analyze each key statement from a transcript.
Workflow:
[Load Meeting]
│
▼
[Select Many: json.callRecording.keyStatements]
│
▼
[If: json.statement.classification == "concern"]
├── True ──▶ [Flag for Review]
└── False ─▶ [Sink]
Configuration:
- Items:
json.callRecording.keyStatements - Label:
statement
Example: Expand with Context (using Broadcast)
Process items while keeping meeting context.
Workflow:
[Load Meeting] ──┬──▶ (meeting data) ─────────┐
│ ├──▶ [Broadcast] ──▶ [Email]
└──▶ [Select Many: attendees]┘
Broadcast Configuration:
- Broadcast:
left - Left Label:
meeting - Right Label:
attendee
Email Template:
Hi {{ json.attendee.name }},
Thank you for "{{ json.meeting.title }}".
Example: Nested Array Expansion
Expand arrays within arrays (two Select Many nodes):
Workflow:
[Load Data]
│
▼
[Select Many: json.deals]
│
▼
[Select Many: json.deal.contacts]
│
▼
[Email Send]
Note: This creates emails for each contact in each deal.
Best Practices
1. Use Labels for Clarity
Always set a label for readability:
✅ With label:
{{ json.attendee.name }}
{{ json.attendee.email }}
❌ Without label:
{{ json.name }}
{{ json.email }} // Less clear what "json" represents
2. Check Array Existence
Before Select Many, verify the array exists:
[If: json.items.size() > 0]
├── Yes ──▶ [Select Many] ──▶ [Process]
└── No ───▶ [Sink]
Or handle empty arrays gracefully.
3. Consider Downstream Volume
Each item creates a separate execution path:
- 100 attendees = 100 downstream executions
- Plan for the impact on action nodes
4. Preserve Context When Needed
If downstream needs original context, use Broadcast:
[Load] ──┬──▶ [context] ──────────┐
│ ├──▶ [Broadcast] ──▶ [Has both]
└──▶ [Select Many] ──────┘
5. Use Meaningful Array Paths
Be specific about which array to expand:
✅ Specific:
json.meeting.attendees
❌ Ambiguous:
json.items // What items?
Common Issues
"Cannot iterate over non-array" error
Cause: Items expression doesn't evaluate to an array
Solutions:
- Verify the path is correct
- Check upstream data actually has the array
- Use
has()to check existence
No outputs from Select Many
Cause: Array is empty
Solution: Handle empty arrays:
[If: array has items?]
├── Yes ──▶ [Select Many]
└── No ───▶ [Handle empty case]
Too many downstream executions
Cause: Large array creating many items
Solutions:
- Filter array upstream before Select Many
- Limit array size
- Use batch processing instead
Lost context after Select Many
Cause: Original data not preserved
Solution: Use Broadcast to attach context:
[original] ──┬──▶ [context] ───────────┐
│ ├──▶ [Broadcast]
└──▶ [Select Many: items] ┘
Related Nodes
- Broadcast - Add context to expanded items
- Zip - Recombine after parallel processing
- If - Filter items conditionally
Technical Details
Execution Behavior
Per-item execution means:
- Each array element triggers downstream independently
- 10 elements = 10 separate downstream executions
- Order is preserved
Output Count
Number of outputs = length of items array:
- Empty array → no outputs (downstream doesn't execute)
- 1 element → 1 output
- N elements → N outputs
Memory Efficiency
Select Many doesn't duplicate the entire input:
- Only the specified array is expanded
- Each output contains one array element
- Original container data is not included (use Broadcast if needed)
Array Types
Works with arrays of any type:
- Objects:
[{a: 1}, {a: 2}] - Primitives:
["a", "b", "c"] - Mixed: Not recommended but supported