Technique · EXTRA
for as List Comprehension: map + filter in One Line
The supply, filter, process trio lets one for do what takes other tools two passes: filter first, then convert one by one.
Open the extra →On the results screen, the loot rolls out one piece at a time — that is exactly the rhythm of a ForEach Loop. This lesson covers using a ForEach Loop to call roll on an Array element by element, counting fixed rounds with 0..N, adding a filter right on the node, and finally unlocks the hidden skill: when Verse's for finishes, it hands you a brand-new array — as if your ForEach came with a built-in "auto-collect each round's result" output.
Open the results screen and the loot list scrolls from top to bottom: wooden sword, potion, gold pouch… As each item gets its name called, it walks through the same routine — show it, stash it, count it. "Do the same thing once for every element in a collection" — that is for's entire mission.
Think of Verse's for as the Blueprint ForEach Loop node: wire an Array into its Array pin and it spits out elements one by one from the Array Element pin; for each one, the chain of nodes downstream of Loop Body runs once, and once everything is out, execution automatically continues from the Completed pin. The name you give "this round's element" in the parentheses (here, Item) is the equivalent of ForEach's Array Element output pin — only reachable inside the loop body. The old-school loops from other languages, where you write your own counter and increment it by hand, simply do not exist in Verse — and you will not miss them, just like a Blueprint ForEach never asks you to count by hand.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
loot_device := class(creative_device):
# Loot list: an array of strings
Loot:[]string = array{"Wooden Sword", "Potion", "Gold Pouch"}
OnBegin<override>()<suspends>:void =
for (Item : Loot):
Print("Picked up: {Item}")
Node-for-node translation: this snippet wires the Loot string array into ForEach's Array pin, puts one Print String node in the Loop Body, and prints "Picked up: __" with each round's Item to the top-left of the screen; the array holds three items, so ForEach runs that Print String three times.
Three pieces of loot, three passes through the loop body, and each round Item swaps to the next treasure. Remember for's personality: it is bounded iteration — however many elements the collection holds, that is how many rounds it runs, then it clocks out automatically, never spinning one lap extra. Want to spin forever? That is loop territory — see you next lesson. Also, Blueprints never make you agonize over "indentation or braces" — whatever nodes the Loop Body pin connects to, that is the loop body; this is purely a text-formatting detail you only worry about when writing Verse as text.
Sometimes getting the element is not enough — you also want to know "which slot is this?". Good news: you already know the Blueprint ForEach Loop — besides Array Element it also has an Array Index output pin, counting from 0. The Verse equivalent is to add an index name in front of the element name (written Index -> Item); that Index is exactly ForEach's Array Index pin, and the right side is still the element itself. One roll call, double the intel.
Another common need is "run exactly N rounds" — turn counts, say. Blueprints have a dedicated node for this: the plain For Loop node — fill in First Index and Last Index and it counts from one end to the other, handing you an Index each round. In Verse you write it as a range like 0..3. Two traps to memorize: first, ranges include both ends — 0..3 is 0, 1, 2, 3, four rounds total, and 0..10 is 11 rounds (just like the Blueprint For Loop, it counts up to and including Last Index); the off-by-one is the classic bug of all time. Second, ranges only count whole numbers, always add 1 per step, and only go from small to large — writing one backwards like 10..0 is not an error, but it runs zero rounds (want to count down? The advanced extra at the end has the full answer).
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
roll_call_device := class(creative_device):
Loot:[]string = array{"Wooden Sword", "Potion", "Gold Pouch"}
OnBegin<override>()<suspends>:void =
# Indexed iteration: Index starts at 0
for (Index -> Item : Loot):
Print("Slot {Index}: {Item}")
# Range iteration: 0..3 includes both ends — 4 rounds
for (Round := 0..3):
Print("Round {Round} begins")
One safety fact worth pocketing: running a ForEach over an empty array is harmless in Blueprints, and Verse is just as safe. Even if you use "array length minus one" to decide how far to count (.Length is the equivalent of the Blueprint node that gets an Array's length), an empty array turns the range into "count from 0 to -1" — the start is bigger than the end, so zero rounds run and for skips past quietly, no errors. Empty arrays always get this treatment.
Now let's bolt on indexed iteration and watch a counting machine run three rounds with your own eyes. Click "Run next step" and notice how the two lines carrying Index and Item get visited over and over — like ForEach's Loop Body being re-run again and again: the same chain of nodes, but each round Index and Item have swapped out:
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
loot_counter_device := class(creative_device):
Loot:[]string = array{"Wooden Sword", "Potion", "Gold Pouch"}
OnBegin<override>()<suspends>:void =
Print("Counting up the loot...")
for (Index -> Item : Loot):
Print("Slot {Index}: {Item}")
Print("All counted: {Loot.Length} items.")
Click "Run next step" to watch the code execute line by line.
Only want to show off the "expensive" loot at the results screen? In Blueprints you would hang a Branch node inside the ForEach's Loop Body and shunt the rejects out the False side. Verse saves you the trouble: write the filter condition straight into for's parentheses and skip the standalone Branch entirely. The rule — first in the parentheses must be "the supplier" (an array or a range), followed by any number of filter conditions, or an intermediate variable named on the spot:
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
filter_device := class(creative_device):
Prices:[]int = array{5, 120, 48, 300, 9}
OnBegin<override>()<suspends>:void =
# Filter: only handle loot priced above 100
for (P : Prices, P > 100):
Print("Valuable stored: {P} gold")
# Inline definition + filter: compute Taxed first, then filter
for (P : Prices, Taxed := P * 2, Taxed > 200):
Print("Past 200 after doubled tax: {Taxed}")
Node-for-node translation: the first loop says "ForEach over the Prices array with a P > 100 check built into the loop header (as if the Branch got stuffed into the header itself); only the entries above 100 ever reach Print String". The second adds an intermediate variable Taxed (price times 2 first), then compares it against 200 as the filter — like wiring a multiply node and then a Branch inside the ForEach.
Here comes the key rule: the filters in for's parentheses are judged one round at a time — if P > 100 does not pass on some round, only that round is skipped (a Branch taking the False pin: this one item just is not processed). It never flips the whole loop over, and it never makes the loop "fail as a whole". In the first loop above, 5, 48, and 9 are silently skipped while 120 and 300 get stored as normal. All the pass-or-fail checks from last lesson (comparing values, fetching an array element by index, and so on) work here — stack as many as you like.
But this filter has one house rule: it must be possible to "try it and cleanly take it back", so you cannot put Print or similar "once it runs, the screen has changed, no take-backs" nodes inside the filter — Compile lights up red with This invocation calls a function that has effects that are not allowed by its context. Printing, opening doors, and other "it happened, it counts" actions go in the loop body — the body has no pass-or-fail restriction, Print away. Conversely, if the loop body wants a "might come up empty" operation (like fishing Prices[I] out of the array by index — out of bounds means an empty catch), you have to guard it with a Branch of your own first. In one sentence: parentheses filter, body works.
Time to reveal the coolest move of the lesson. In Verse, when for finishes it does not just say "done, ran my rounds" — it also hands you every round's output gathered into a new array, as if your Blueprint ForEach came with an automatic "Add each round's result to a new Array" output wired in — except you never have to create that array or call Add yourself. The official wording: "the loop evaluates to an array of values, so you can quickly create new arrays based on existing arrays." Every round that succeeds contributes the value its body computed, in order, to the result array; the rounds that got filtered out contribute nothing.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
forge_device := class(creative_device):
Prices:[]int = array{5, 120, 48, 300, 9}
OnBegin<override>()<suspends>:void =
# Range + transform: produces array{1, 4, 9, 16, 25}
Squares := for (X := 1..5) { X * X }
Print("Got {Squares.Length} square numbers")
# Generator + filter + transform: filter and double in one line
Doubled := for (P : Prices, P > 100) { P * 2 }
Print("Double-reward list has {Doubled.Length} entries")
Node-for-node translation: the first line, for (X := 1..5) { X * X }, is a For Loop counting 1 to 5, squaring the Index each round, with the results auto-collected into the array array{1, 4, 9, 16, 25} stored in Squares. The second line is a ForEach over Prices with an above-100 filter; whatever passes gets multiplied by 2 and collected into Doubled — iterate, filter, process, and build a new array, all in one breath.
In that second line, the supplying array puts out elements, the filter condition weeds out the cheap stuff, and the braces do the processing — a job other tools need two passes for ("filter first, then convert one by one"), one Verse for does whole. A reminder while we're here: if the processing step itself "might not compute" (fishing Arr[I] out by index, or last lesson's failable integer division), do not force it into the braces — there is no safety net there. Move it into the parentheses as an intermediate variable and let the "one round, one judgment, skip on failure" machinery catch it: rounds that cannot compute get dropped automatically. Empty arrays are no cause for panic either: zero rounds run and for dutifully hands over an empty array array{}.
One last jab, on the "snapshot" idea: Verse arrays are passed by value, and for photographs the collection the instant it starts. If you later use a Set node inside the loop body to change that array variable, the ongoing iteration keeps following the snapshot, completely unaffected — the right way to "modify while iterating" is to collect a new array with a for expression first, then write it back with a single Set node after the whole loop finishes. Also a performance note: for runs in one breath (every round crammed into the same frame, no pausing for air); heavy work over a huge array can trip the engine's infinite-loop protection and error out. The craft of spreading work across several frames (the Delay or per-Tick batching tricks you know from Blueprints) comes next lesson.
for does not have much syntax, but its pitfalls all have personality. Let's sweep the minefield in advance:
:=, array iteration uses :, and getting it wrong (say, a lone =) is a Compile error. Wiring Blueprints you never touch these symbols, but the moment you write Verse you need to know which is whose: one peg, one hole.10..0 — no error, no warning; the Loop Body quietly never runs a single round. Debugging this one costs the most hair.Minefield cleared — time for hands-on practice, and this one trains your feel for reading and writing Verse. The patrol machine below has two symbols carved out: one is the symbol that binds a range iteration, the other is the "greater than" comparison. Fill them back in and click "Check answer":
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
patrol_device := class(creative_device):
Scores:[]int = array{55, 72, 91, 38, 87}
OnBegin<override>()<suspends>:void =
# Range iteration: 0..2, three rounds (which symbol binds a range?)
for (Round ____ 0..2):
Print("Patrol round {Round}")
# Filter clause: only report scores above 60
for (S : Scores, S ____ 60):
Print("High score: {S}")
Blueprint splits “looping” across several differently shaped nodes; Verse has exactly one for. This table pairs them up, and the differences column explains what the merge gained you — and what it cost.
| What you do in Blueprint | How you write it in Verse | Difference |
|---|---|---|
| A ForEach Loop node with an array on its Array pin | for (Item : Loot): |
The name to the left of : is the Array Element pin, visible only inside the loop body |
| The Array Index output pin on ForEach Loop | for (Index -> Item : Loot): |
The index isn’t free — you only get it if you write it; index on the left of the arrow, element on the right, both from one roll call |
| A For Loop node with First Index / Last Index filled in | for (Round := 0..3): |
Both are inclusive at both ends, so that much matches; but a range binds with := (array iteration uses :), and a backwards 10..0 raises no error and simply runs zero laps |
| The Loop Body and Completed execution pins | The indented block is Loop Body; the line back at the original indent is Completed | Blueprint tells “inside the loop” from “after the loop” by which pin you wired — miswire it and you get a silent bug; Verse tells them apart by indentation, and getting it wrong simply won’t compile |
| A Branch inside Loop Body to reject the elements you don’t want | for (P : Prices, P > 100): |
The filter moves up into the loop header, sparing you a separate Branch; it’s judged once per lap, and a lap that fails is simply skipped |
| A ForEach Loop with Break node | No counterpart — writing break inside a for is a straight compile error |
Verse hands “finish early” to next lesson’s loop, or to wrapping the logic in a function and returning out of it; there’s no continue either — to skip a lap, use the filter in the parentheses |
| Make an empty Array, then Add to it each lap to collect results | Squares := for (X := 1..5) { X * X } |
Verse’s for is itself an expression: when it finishes it hands you the assembled array, collapsing Blueprint’s three steps (make, Add, read back) into one line |
The merge works because Blueprint’s several loop nodes were always the same act in different shells: “take a series of things, walk them one at a time”. The only real variation is where the series comes from — an array, or a run of consecutive numbers. Verse writes that source into the parentheses, so one for suffices; and while it was at it, the filter moved into the same parentheses, sparing you the Branch nearly everyone wires up in a Blueprint loop body.
What you lose is Break. ForEach with Break is handy in Blueprint, and Verse deliberately withholds it — because for is defined as bounded iteration: the lap count is fixed the moment it starts, and bailing out midway would break its promise to hand back an array when it finishes. If you need a loop you can walk out of, that’s next lesson’s loop.
The list has finished rolling — three mini-stages to check your build. Zero penalty for wrong answers; retry as often as you like.
A ForEach iterates Scores with an "S must be greater than 40" filter. What happens when it reaches the item where S = 12?
A for counts from 1 to 3, squares the current number each round, and collects the results into an array (written Squares := for (X := 1..3) { X * X }). When it finishes, what is Squares?
You want a for to exit early once it finds its target. What is the right approach?
Technique · EXTRA
The supply, filter, process trio lets one for do what takes other tools two passes: filter first, then convert one by one.
Open the extra →Deep Dive · EXTRA
Map iteration hands you keys, not indexes; if you want a running number, there is no official shortcut — you count it yourself.
Open the extra →Advanced · EXTRA
0..10 cannot be stored in a variable — one restriction that reveals a deliberate Verse language-design trade-off.
Open the extra →