Verse Wiki — the Verse handbook for Blueprint authors
Deep Dive · EXTRA

for as List Comprehension: map + filter in One Line

Work that takes other tools a two-step relay — filter one pass, then convert one by one — a single Verse for wraps up in passing, auto-collecting the results into a new array while it's at it. This page takes the supply / filter / process trio apart for a close look, then stages a head-to-head against the honest old "make an array by hand and Add to it inside the loop" style.

1. The Trio: the Generator Supplies, the Filter Gatekeeps, the Body Processes

The official docs pin for down in one key sentence: "the loop evaluates to an array of values, so you can quickly create new arrays based on existing arrays." In plain speech: for collects each round's output as it iterates and hands you a finished new array at the end — as if a Blueprint ForEach came with an automatic "Add each round to a new Array" output wired in. The trio splits the work like this: the array or range at the head of the parentheses supplies the goods, the filter conditions after it gatekeep (a Branch standing guard), and the processing inside the braces turns each item into its new shape — results gathered into a new array automatically, no hand-made array, no manual Add, ever.

report_device.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }

report_device := class(creative_device):

    Scores:[]int = array{55, 72, 91, 38, 87}

    OnBegin<override>()<suspends>:void =
        # Pure filter: keep passing grades only -> array{72, 91, 87}
        Passed := for (S : Scores, S >= 60) { S }

        # Pure map: double everyone -> array{110, 144, 182, 76, 174}
        Doubled := for (S : Scores) { S * 2 }

        # map + filter one-liner: double the passing grades -> array{144, 182, 174}
        Boosted := for (S : Scores, S >= 60) { S * 2 }

        Print("{Passed.Length} passed, {Boosted.Length} entries on the boost list")

Node-for-node translation: the three lines are pure filtering, pure processing, and filter-plus-process. Passed = a ForEach over Scores with a "greater than or equal to 60" check, collecting whatever passes, unchanged, into a new array; Doubled = no filter, collect each element times 2; Boosted = filter for "at least 60" first, then multiply the survivors by 2 and collect. That is Blueprint's ForEach into Branch into multiply node, then Add the result to a fresh Array — except Verse writes it all in one line.

Notice the information density of the Boosted line: iterate, filter, process, and build a new array — four jobs in one line, and not a single "gets-changed-along-the-way" intermediate variable anywhere. The two rounds that failed the check (38 and 55) simply never enter the result array — this is Lesson 14's "one round, one judgment" showing up on the output side: no pass means no goods, no goods means nothing enters the new array, and you saved even the Branch. So all those pass-or-fail checks — comparing values, fetching array elements by index like Arr[I] — can be piled straight into the parentheses.

The parentheses hold more than filters. The rule: the head must be the supplying array or range, followed by any number of filter conditions — or an intermediate variable named on the spot. Meaning you can compute a middle value, give it a name, then filter on it: for (S : Scores, D := S * 2, D >= 120) { D } — work out the doubled score as D first, then let D face the gate; names created earlier stay usable further along. That line does exactly what Boosted does, but once the processing gets complicated, splitting out intermediate variables spares you from squeezing every calculation into one expression.

2. Head-to-Head: Imperative Accumulation vs the One-Line Expression

Without for's auto-collection, the same job can of course still be done — the routine is Blueprint's most familiar move: "create an empty Array variable outside the loop, then Add into it one by one inside the Loop Body":

imperative_style.verse
# Imperative style: it runs, but every line is a door left open for bugs
var Collected:[]int = array{}
for (S : Scores):
    if (S >= 60):
        set Collected += array{S}

Node-for-node translation: first create an empty array variable Collected (Make Array with nothing in it, stored in a variable), ForEach over Scores, a Branch in the Loop Body checking "at least 60", and the survivors go through set Collected += array{S} (an Add node pushing S into Collected). Four nodes chained up — just to equal what the one-line for above does.

Four lines against one, and the gap is more than length. The manual version adds a "half-finished product that keeps getting poked", Collected: empty before the loop starts, half-full at every instant during it, and if some node elsewhere reads or writes it at the wrong moment, hello bug. The for version has no such window — Passed is born complete, and it is a locked constant: there is no Set node to wire, so nothing can touch it. This is Verse at its most comfortable: you just say what you want, and skip directing how to gather it up piece by piece.

When should you still write the loop the honest way? When each round's job is an "it happened, it counts" action (printing, opening doors, granting rewards) rather than producing data — put the action in the Loop Body. And when the filtering and processing grow too big for one line, split out several intermediate variables or even fall back to the plain "Branch inside a ForEach" style — whatever reads best wins. The one-liner is a tool, not a religion.

3. Common Pitfalls: Three Ways the One-Liner Flips Over

Logging inside the parenthesized filter. The filter must be try-then-take-back safe, and Print String — a "once it runs, the screen has changed, no take-backs" node — is not; Compile goes red with "This invocation calls a function that has effects that are not allowed by its context". Want to peek at intermediate values? Move the Print String into the Loop Body: no such restriction there, side effects welcome.

Assuming one failed round halts the line. A failed filter just means that round produces nothing and the next round begins (a Branch taking False) — it neither aborts the whole for nor makes for itself fail. Conversely, "stop early once I have enough" is also off the table — break belongs only to loop, and writing it in a for gets a Compile error; if you truly need a mid-run exit, switch to loop with an index you maintain yourself (Lesson 15's home turf), or move the for into a function and leave by ending the function.

Worrying an empty array will explode. It will not. When the supplying array has no goods at all, the processing never runs once, and for quietly hands over an empty array; asking for its .Length or iterating it again afterwards works as normal — using a one-liner on an empty array needs no length check first.

4. Pop Quiz

A for iterates array{1, 2, 3, 4} with an "X greater than 2" filter and collects the survivors times 10. When it finishes, what is Result?

Sources

Compiled from the official Epic documentation: Control Flow in Verse ↗ and the Verse Language Quick Reference ↗.