Python-Style Loop Patterns in ABAP
LLMs trained on Python tend to generate index-based loops — for i in range(len(lst)) translated into ABAP's DO/READ TABLE ... INDEX combo. ABAP has far better tools for this.
The Problem
Python's for i in range(len(my_list)) is a common pattern when you need an index counter alongside the value. LLMs internalize this pattern deeply. When asked to generate ABAP, they reproduce its structure: a counter variable, a loop that increments the counter, and an explicit index-based lookup.
In ABAP, this is almost never the right approach. ABAP's LOOP AT statement was designed for iterating internal tables — it handles the index transparently via SY-TABIX, and provides ASSIGNING and REFERENCE INTO for direct access without unnecessary copying.
What It Looks Like
Here is the pitfall pattern — a manually managed index counter with DO ... TIMES and READ TABLE ... INDEX:
REPORT ztest_pitfall2.
TYPES: BEGIN OF ty_item,
name TYPE string,
qty TYPE i,
END OF ty_item.
DATA lt_items TYPE STANDARD TABLE OF ty_item WITH DEFAULT KEY.
DATA ls_item TYPE ty_item.
" Populate table
ls_item-name = 'Widget A'. ls_item-qty = 10. APPEND ls_item TO lt_items.
ls_item-name = 'Widget B'. ls_item-qty = 5. APPEND ls_item TO lt_items.
ls_item-name = 'Widget C'. ls_item-qty = 20. APPEND ls_item TO lt_items.
" Python-style: manual index loop
DATA lv_index TYPE i.
lv_index = 1.
DO LINES( lt_items ) TIMES.
READ TABLE lt_items INTO ls_item INDEX lv_index.
WRITE: / ls_item-name, ls_item-qty.
lv_index = lv_index + 1.
ENDDO.
This actually works — but it is the ABAP equivalent of writing Python as if it were C. The intent of the code is obscured by the ceremony of managing an index manually.
Why It's a Problem
Verbose and harder to read
Four lines of loop scaffolding replace what should be a single LOOP AT statement. The intent — "iterate over all items" — is buried under the mechanics.
Off-by-one risk
Manually managed index counters are a classic source of off-by-one bugs. Starting at 0 instead of 1 (Python vs ABAP convention) is an error LLMs sometimes make here.
Unnecessary data copy
READ TABLE ... INTO ls_item copies the row every iteration. LOOP AT ... ASSIGNING FIELD-SYMBOL gives you a direct reference — no copy, and in-place modification is possible.
Misses ABAP idioms entirely
ABAP's loop system supports inline filtering (WHERE), group-by (GROUP BY), and early exit (EXIT/CHECK). The index-based pattern forfeits all of these.
The Fix
Use LOOP AT ... INTO for read-only access with a work area copy, or LOOP AT ... ASSIGNING FIELD-SYMBOL for direct reference (preferred for modification or performance-sensitive loops).
REPORT ztest_correct2.
TYPES: BEGIN OF ty_item,
name TYPE string,
qty TYPE i,
END OF ty_item.
DATA lt_items TYPE STANDARD TABLE OF ty_item WITH DEFAULT KEY.
" Populate table
lt_items = VALUE #(
( name = 'Widget A' qty = 10 )
( name = 'Widget B' qty = 5 )
( name = 'Widget C' qty = 20 )
).
" Option 1: LOOP INTO work area (copy per row)
DATA ls_item TYPE ty_item.
LOOP AT lt_items INTO ls_item.
WRITE: / ls_item-name, ls_item-qty.
ENDLOOP.
" Option 2: LOOP ASSIGNING field-symbol (direct reference, no copy)
FIELD-SYMBOLS: <ls_item> TYPE ty_item.
LOOP AT lt_items ASSIGNING <ls_item>.
WRITE: / <ls_item>-name, <ls_item>-qty.
ENDLOOP.
" Option 3: inline DATA declaration (ABAP 7.40+)
LOOP AT lt_items INTO DATA(ls_inline).
WRITE: / ls_inline-name, ls_inline-qty.
ENDLOOP.
When do you actually need the index? If you genuinely need the row number, SY-TABIX is available inside any LOOP AT automatically. You never need to manage a counter variable manually.
Side-by-Side Comparison
DATA lv_index TYPE i.
lv_index = 1.
DO LINES( lt_items ) TIMES.
READ TABLE lt_items
INTO ls_item
INDEX lv_index.
" ... process ls_item ...
lv_index = lv_index + 1.
ENDDO.
LOOP AT lt_items
ASSIGNING FIELD-SYMBOL(<item>).
" ... process <item> ...
" SY-TABIX = current index
" (available automatically)
ENDLOOP.
Same result. The idiomatic version is shorter, clearer, and avoids copying each row.
ASSIGNING vs INTO — Which to Use?
| Situation | Use |
|---|---|
| Read-only iteration, small rows | LOOP AT ... INTO DATA(ls) |
| Modifying rows in-place | LOOP AT ... ASSIGNING <fs> |
| Large rows, performance matters | LOOP AT ... ASSIGNING <fs> |
| Need a ref for later use outside the loop | LOOP AT ... REFERENCE INTO DATA(lr) |
Run the pitfall code in ABAP Dojo's AI Validator to see the pattern detected live.
Validate this code in ABAP Dojo →