FOR — the loop that lives inside an expression¶
Level: 201 · working knowledge
One line: FOR turns one table into another inside a VALUE, REDUCE or NEW expression — mapping, filtering with WHERE, or counting with UNTIL/WHILE — and the loop variable it declares exists only inside that expression.
Map¶
Read it right to left: for each row of lt_orders, produce one row holding ls_o-id. The equivalent LOOP … APPEND … ENDLOOP is four lines and leaves a work area behind; this is one expression and leaves nothing.
ls_o is declared by the FOR itself. It is not visible after the closing parenthesis, which is the one place ABAP does give you a scope narrower than the program.
Filter¶
The WHERE condition sits in parentheses and follows internal-table WHERE rules — a component compared to a value. You cannot calculate in it: WHERE ( amount * 2 > 100 ) is a syntax error, not a slow filter. FILTER does the same job in a shorter form when the table has a suitable key; FOR … WHERE works on any table.
Count¶
UNTIL stops when the condition becomes true; WHILE continues as long as it is true. THEN sets the step — FOR lv_i = 10 THEN lv_i - 2 UNTIL lv_i < 0 counts down by twos. There is no FOR i = 1 TO 5 form; the condition is always written out.
Nesting is a cross product¶
DATA(lt_labels) = VALUE ty_names( FOR ls_p IN lt_orders
FOR lv_n = 1 UNTIL lv_n > 2
( |{ ls_p-id }-{ lv_n }| ) ).
Two FOR clauses in a row mean every combination — rows × 2 here. This is the construct's sharpest edge: a nested FOR over two tables of a thousand rows builds a million-row table in one statement that fits on one line. See Performance.
LET, in passing¶
LET names a value once inside the expression so it is not recomputed or repeated. It is covered on its own page — LET — and appears in the program below.
z_kw_for.prog.abap — pasted here by tools/check_examples.py. Syntax-checked, never run: abaplint parses and type-checks it against the release in abaplint.json, but no system has produced output for it, so this page shows none.
REPORT z_kw_for.
TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2.
TYPES: BEGIN OF ty_order,
id TYPE i,
region TYPE string,
amount TYPE ty_amount,
END OF ty_order.
TYPES ty_orders TYPE STANDARD TABLE OF ty_order WITH EMPTY KEY.
TYPES ty_ints TYPE STANDARD TABLE OF i WITH EMPTY KEY.
TYPES ty_names TYPE STANDARD TABLE OF string WITH EMPTY KEY.
DATA(lt_orders) = VALUE ty_orders( ( id = 1 region = `EU` amount = '100.00' )
( id = 2 region = `US` amount = '250.50' )
( id = 3 region = `EU` amount = '75.25' ) ).
" Map: one output row per input row.
DATA(lt_ids) = VALUE ty_ints( FOR ls_o IN lt_orders ( ls_o-id ) ).
" Filter: WHERE decides which input rows are visited at all.
DATA(lt_eu) = VALUE ty_orders( FOR ls_e IN lt_orders WHERE ( region = `EU` ) ( ls_e ) ).
" No table to walk: an index of your own, counting with UNTIL or WHILE.
DATA(lt_squares) = VALUE ty_ints( FOR lv_i = 1 UNTIL lv_i > 5 ( lv_i * lv_i ) ).
" Nested FOR is a cross product, read left to right.
DATA(lt_labels) = VALUE ty_names( FOR ls_p IN lt_orders
FOR lv_n = 1 UNTIL lv_n > 2
( |{ ls_p-id }-{ lv_n }| ) ).
" LET names a value once so the expression does not repeat itself.
DATA(lt_tagged) = VALUE ty_names( LET lv_tag = `order` IN
FOR ls_t IN lt_orders ( |{ lv_tag } { ls_t-id }| ) ).
WRITE: / 'ids ', lines( lt_ids ).
WRITE: / 'eu ', lines( lt_eu ).
WRITE: / 'squares ', lines( lt_squares ).
WRITE: / 'labels ', lines( lt_labels ).
LOOP AT lt_tagged INTO DATA(lv_label).
WRITE: / lv_label.
ENDLOOP.
If you are coming from another language¶
- Python. This is the list comprehension, including the
WHEREas the trailingifand nesting as the cross product.UNTILplays the part ofrange(), spelled as a condition. - Rust.
iter().map(…)and.filter(…)collected into aVec. ABAP's version is not lazy: it builds the whole table. - SQL.
FOR … IN … WHEREis aSELECT … FROM … WHEREover a table you already hold in memory — which is worth noticing, because if the rows came from the database the filter usually belonged there instead. See Open SQL.
See also¶
VALUE— the expressionFORmost often lives inREDUCE—FORfolding many rows into one valueFILTER— the shorter form, when the table has the key for itLOOP AT— the statement, and when it is still the right choice- Performance — what a nested
FORcosts - Constructor expressions — the
VALUE/NEW/CONDfamily as a family