🔠 String ⁄ List Operations
One node for 27 kinds of string and list surgery
- input
- aux1
- aux2
- aux3
- output
- result
🔠 String ⁄ List Operations is the swiss-army knife of this pack, and honestly the node you'll probably reach for most. It's one node that does 27 different string and list manipulations - split, join, replace, slice, find, count, case-convert, random picks, comparisons - so instead of stacking five nodes from five different packs to process a filename or a prompt, you set one dropdown. It's not a replacement for a proper text-processing pack; it's the thing that stops you from needing one for 90% of what comes up in a generation workflow.
How it works
You give it an input (any type), pick an operation from the dropdown, and it hands you two outputs: output (the value after processing) and result (the verdict, index, or count). The result semantics change per operation, and that's the one thing that trips people up - read on.
Every operation runs through the same plumbing. start_from_end flips direction (prepend instead of append, last occurrence instead of first, reversed lists). case_insensitive makes case-sensitive ops - comparisons, find, replace, start/ends-with - ignore case via casefold, so "FLUX" matches "flux". The mechanism underneath is plain Python, and it shows: SLICE is literally Python slice notation, negative indexes and all.
The one real quirk to understand: you have two ways to feed an operation its arguments. The aux1/aux2/aux3 slots accept any type (wire a number, a list, whatever). The param1/param2/param3 text boxes are string-only but take priority - if a param is non-empty, it overrides the corresponding aux slot. So for quick string arguments you type them in; for non-string arguments (like an integer count) you wire the aux slot.
The inputs that matter
- input (required,
*) - the string or list you're processing. - operation (required, 27 choices) - everything else is flavor.
- start_from_end, case_insensitive (required, booleans).
- aux1–3 (optional, any type) and param1–3 (optional, strings) - per-operation arguments.
Operations you'll actually use
- SPLIT / SPLIT_LINES - break a string into a list by a delimiter or by lines. The workhorse for parsing prompts and wildcards.
- JOIN - the reverse: glue a list into a string with a separator.
- REPLACE - substring swap, per element for lists.
- SLICE - cut out a range with Python slice notation (negative values work).
- FIND - get the index of a substring;
-1if absent. Wire the result into a condition to ask "is this string in there?" - LENGTH - count characters or elements.
- CONCATENATE - glue two or more strings/lists together.
- TRIM_SPACES / STRIP - clean up whitespace or specific characters off the ends.
- LOWERCASE / UPPERCASE / PROPERCASE - fix messy model names and tags.
- RANDOM_INPUT / RANDOM_ELEMENT - pick a random prompt from a list, or a random line. Great for variation batches.
Beware the semantics of result. COMPARE doesn't return a simple boolean - it returns a binary sum (1 if aux1 matches, 2 if aux2, 3 if both, 7 if all three). GENERATE, REPLACE, GET_LINE return True/False for success. SLICE and SPLIT return a length. Most operations leave output unchanged when they're pure tests (like IS_NUMERIC or STARTS_WITH), so check which output actually carries your answer before wiring blind. And TO_STRING will raise an error if it can't convert - it doesn't fail gracefully.
Why it's worth having
Every serious workflow eventually needs to munge text: extract a checkpoint name from Model Selector output, build a save filename, parse a LoRA trigger list, pick a random prompt per iteration. This node folds all of that into one place, and it chains - feed one output into the next node's input and you can build fairly elaborate text pipelines without touching a single other pack. If you're pairing it with this pack's IF selector and Universal Switch, you can even do string-driven branching: test a filename with ENDS_WITH, feed the boolean into a condition, and route whole workflow sections from text.
Install and gotchas
Same as the rest of the pack - no Python dependencies:
cd ComfyUI/custom_nodes
git clone https://github.com/VykosX/ControlFlowUtils
or ComfyUI Manager → search ControlFlowUtils → install → restart.
Two real-world gotchas. First, the 27 options are a lot to stare at; if an operation's semantics are fuzzy, the author's own tooltips on each aux/param slot describe what that operation needs. Second, remember the param-overrides-aux priority - if you have a stale value in a param box, it'll silently override the wire you attached to the aux slot. When something "doesn't do what I wired it to do," check the param boxes first.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| input | * | Input String or List to process | |
| operation | COMBO | Operation to perform on the Input | |
| start_from_end | BOOLEAN | Whether to evaluate from the start or the end of the expression | |
| case_insensitive | BOOLEAN | Ignore case sensitivity for operations that support it | |
| aux1opt | * | Auxiliary Input 1 (Purpose varies based on operation) | |
| aux2opt | * | Auxiliary Input 2 (Purpose varies based on operation) | |
| aux3opt | * | Auxiliary Input 3 (Purpose varies based on operation) | |
| param1opt | STRING | Will be used instead of Aux1 if not empty (Purpose varies based on operation) | |
| param2opt | STRING | Will be used instead of Aux2 if not empty (Purpose varies based on operation) | |
| param3opt | STRING | Will be used instead of Aux3 if not empty (Purpose varies based on operation) |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| output | * | The Input value after being processed by the chosen operation |
| result | * | Result of the chosen operation |