ABAP FOR: Iteracion inline en expresiones

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

La expresion FOR permite iteracion inline dentro de expresiones constructoras como VALUE, REDUCE y NEW. Reemplaza muchas construcciones LOOP AT ... APPEND con sintaxis compacta y funcional.

Sintaxis

1. FOR … IN (Iteracion de tabla)

FOR <variable> IN <tabla> [ INDEX INTO <indice> ] [ WHERE ( <condicion> ) ]

2. FOR … UNTIL/WHILE (Iteracion condicional)

FOR <variable> = <valor_inicial> [ THEN <expresion> ] UNTIL <condicion>
FOR <variable> = <valor_inicial> [ THEN <expresion> ] WHILE <condicion>

3. FOR GROUPS (Agrupacion)

FOR GROUPS <grupo> OF <variable> IN <tabla>
GROUP BY <expresion_agrupacion>

Principio basico

FOR se usa dentro de VALUE, REDUCE o NEW:

" Crear tabla desde otra tabla
DATA(lt_result) = VALUE ty_result_tab(
FOR ls_source IN lt_source
( campo1 = ls_source-campo1 campo2 = ls_source-campo2 )
).

Ejemplos

1. Transformacion simple de tabla

TYPES: BEGIN OF ty_person,
id TYPE i,
name TYPE string,
age TYPE i,
END OF ty_person,
ty_persons TYPE TABLE OF ty_person WITH EMPTY KEY.
TYPES: ty_names TYPE TABLE OF string WITH EMPTY KEY.
DATA: lt_persons TYPE ty_persons.
lt_persons = VALUE #(
( id = 1 name = 'Juan' age = 30 )
( id = 2 name = 'Ana' age = 25 )
( id = 3 name = 'Pedro' age = 35 )
).
" Extraer solo nombres
DATA(lt_names) = VALUE ty_names(
FOR ls_person IN lt_persons
( ls_person-name )
).
" Resultado: Juan, Ana, Pedro

2. Comparacion: FOR vs LOOP

" === CLASICO CON LOOP ===
DATA: lt_names_classic TYPE ty_names.
LOOP AT lt_persons INTO DATA(ls_p).
APPEND ls_p-name TO lt_names_classic.
ENDLOOP.
" === MODERNO CON FOR ===
DATA(lt_names_modern) = VALUE ty_names(
FOR ls_p IN lt_persons
( ls_p-name )
).

3. FOR con WHERE (Filtrar)

" Solo adultos (age >= 18)
DATA(lt_adults) = VALUE ty_persons(
FOR ls_person IN lt_persons
WHERE ( age >= 18 )
( ls_person )
).
" Condicion mas compleja
DATA(lt_filtered) = VALUE ty_persons(
FOR ls_person IN lt_persons
WHERE ( age >= 25 AND name <> 'Juan' )
( ls_person )
).

4. Transformacion con calculo

TYPES: BEGIN OF ty_product,
id TYPE i,
name TYPE string,
price TYPE p DECIMALS 2,
quantity TYPE i,
END OF ty_product.
TYPES: BEGIN OF ty_order_line,
product_id TYPE i,
total TYPE p DECIMALS 2,
END OF ty_order_line,
ty_order_lines TYPE TABLE OF ty_order_line WITH EMPTY KEY.
DATA: lt_products TYPE TABLE OF ty_product.
lt_products = VALUE #(
( id = 1 name = 'Widget' price = '10.00' quantity = 5 )
( id = 2 name = 'Gadget' price = '25.00' quantity = 3 )
( id = 3 name = 'Gizmo' price = '15.00' quantity = 8 )
).
" Calcular lineas de pedido con precio total
DATA(lt_order_lines) = VALUE ty_order_lines(
FOR ls_prod IN lt_products
( product_id = ls_prod-id
total = ls_prod-price * ls_prod-quantity )
).

5. INDEX INTO - Leer indice de fila

TYPES: BEGIN OF ty_numbered,
position TYPE i,
name TYPE string,
END OF ty_numbered,
ty_numbered_tab TYPE TABLE OF ty_numbered WITH EMPTY KEY.
DATA(lt_numbered) = VALUE ty_numbered_tab(
FOR ls_person IN lt_persons INDEX INTO lv_idx
( position = lv_idx
name = ls_person-name )
).
" Resultado:
" position = 1, name = 'Juan'
" position = 2, name = 'Ana'
" position = 3, name = 'Pedro'

6. FOR anidado (Producto cartesiano)

TYPES: ty_colors TYPE TABLE OF string WITH EMPTY KEY,
ty_sizes TYPE TABLE OF string WITH EMPTY KEY.
TYPES: BEGIN OF ty_variant,
color TYPE string,
size TYPE string,
END OF ty_variant,
ty_variants TYPE TABLE OF ty_variant WITH EMPTY KEY.
DATA: lt_colors TYPE ty_colors,
lt_sizes TYPE ty_sizes.
lt_colors = VALUE #( ( `Rojo` ) ( `Azul` ) ( `Verde` ) ).
lt_sizes = VALUE #( ( `S` ) ( `M` ) ( `L` ) ).
" Crear todas las combinaciones
DATA(lt_variants) = VALUE ty_variants(
FOR lv_color IN lt_colors
FOR lv_size IN lt_sizes
( color = lv_color
size = lv_size )
).
" Resultado: 9 combinaciones (3x3)
" Rojo-S, Rojo-M, Rojo-L, Azul-S, Azul-M, ...

7. FOR … UNTIL (Iteracion condicional)

TYPES: ty_numbers TYPE TABLE OF i WITH EMPTY KEY.
" Generar numeros del 1 al 10
DATA(lt_1_to_10) = VALUE ty_numbers(
FOR i = 1 UNTIL i > 10
( i )
).
" Con THEN para paso
DATA(lt_even) = VALUE ty_numbers(
FOR i = 2 THEN i + 2 UNTIL i > 20
( i )
).
" Resultado: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20

8. FOR … WHILE

" Cuadrados menores que 100
DATA(lt_squares) = VALUE ty_numbers(
FOR n = 1 WHILE n * n < 100
( n * n )
).
" Resultado: 1, 4, 9, 16, 25, 36, 49, 64, 81

9. FOR GROUPS - Agrupacion

TYPES: BEGIN OF ty_sale,
region TYPE string,
amount TYPE p DECIMALS 2,
END OF ty_sale,
ty_sales TYPE TABLE OF ty_sale WITH EMPTY KEY.
DATA: lt_sales TYPE ty_sales.
lt_sales = VALUE #(
( region = 'NORTE' amount = '1000.00' )
( region = 'SUR' amount = '2000.00' )
( region = 'NORTE' amount = '1500.00' )
( region = 'ESTE' amount = '800.00' )
( region = 'SUR' amount = '1200.00' )
).
TYPES: BEGIN OF ty_region_total,
region TYPE string,
total TYPE p DECIMALS 2,
END OF ty_region_total,
ty_region_totals TYPE TABLE OF ty_region_total WITH EMPTY KEY.
" Suma por region
DATA(lt_by_region) = VALUE ty_region_totals(
FOR GROUPS <group> OF ls_sale IN lt_sales
GROUP BY ls_sale-region
( region = <group>
total = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR m IN GROUP <group>
NEXT sum = sum + m-amount
)
)
).
" Resultado:
" NORTE: 2500.00
" SUR: 3200.00
" ESTE: 800.00

10. FOR GROUPS con multiples campos de agrupacion

TYPES: BEGIN OF ty_order,
year TYPE i,
month TYPE i,
category TYPE string,
amount TYPE p DECIMALS 2,
END OF ty_order.
DATA: lt_orders TYPE TABLE OF ty_order.
lt_orders = VALUE #(
( year = 2024 month = 1 category = 'A' amount = 100 )
( year = 2024 month = 1 category = 'A' amount = 150 )
( year = 2024 month = 1 category = 'B' amount = 200 )
( year = 2024 month = 2 category = 'A' amount = 300 )
).
TYPES: BEGIN OF ty_group_key,
year TYPE i,
month TYPE i,
END OF ty_group_key.
" Agrupacion por anio y mes
DATA(lt_monthly) = VALUE ty_region_totals(
FOR GROUPS <grp> OF ls_ord IN lt_orders
GROUP BY ( year = ls_ord-year month = ls_ord-month )
( region = |{ <grp>-year }/{ <grp>-month }|
total = REDUCE p DECIMALS 2(
INIT s = CONV p DECIMALS 2( 0 )
FOR m IN GROUP <grp>
NEXT s = s + m-amount
)
)
).

11. LET para variables locales

TYPES: BEGIN OF ty_display,
id TYPE i,
display TYPE string,
END OF ty_display,
ty_displays TYPE TABLE OF ty_display WITH EMPTY KEY.
DATA(lt_display) = VALUE ty_displays(
FOR ls_person IN lt_persons
LET prefix = 'ID: '
suffix = COND #( WHEN ls_person-age >= 30 THEN ' (Senior)' ELSE '' )
IN
( id = ls_person-id
display = |{ prefix }{ ls_person-id } - { ls_person-name }{ suffix }| )
).

12. FOR en REDUCE

" Suma de todos los importes
DATA(lv_total) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_sale IN lt_sales
NEXT sum = sum + ls_sale-amount
).
" Con filtro WHERE
DATA(lv_north_total) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_sale IN lt_sales
WHERE ( region = 'NORTE' )
NEXT sum = sum + ls_sale-amount
).

13. FOR en NEW (Referencias de datos)

" Crear tabla como referencia
DATA(lr_names) = NEW ty_names(
FOR ls_person IN lt_persons
( ls_person-name )
).
LOOP AT lr_names->* INTO DATA(lv_name).
WRITE: / lv_name.
ENDLOOP.

14. Extender existente (BASE)

DATA: lt_existing TYPE ty_names.
lt_existing = VALUE #( ( `Existente1` ) ( `Existente2` ) ).
" Agregar nuevas entradas
DATA(lt_combined) = VALUE ty_names(
BASE lt_existing
FOR ls_person IN lt_persons
( ls_person-name )
).
" Resultado: Existente1, Existente2, Juan, Ana, Pedro

15. Transformacion compleja con CORRESPONDING

TYPES: BEGIN OF ty_source,
kunnr TYPE string,
name1 TYPE string,
ort01 TYPE string,
END OF ty_source,
BEGIN OF ty_target,
customer_id TYPE string,
customer_name TYPE string,
city TYPE string,
END OF ty_target,
ty_targets TYPE TABLE OF ty_target WITH EMPTY KEY.
DATA: lt_source TYPE TABLE OF ty_source.
lt_source = VALUE #(
( kunnr = '1000' name1 = 'Garcia SA' ort01 = 'Madrid' )
( kunnr = '2000' name1 = 'Lopez SL' ort01 = 'Barcelona' )
).
" Transformacion con CORRESPONDING
DATA(lt_target) = VALUE ty_targets(
FOR ls_src IN lt_source
( CORRESPONDING #( ls_src
MAPPING customer_id = kunnr
customer_name = name1
city = ort01
)
)
).

Variantes FOR en resumen

VarianteUso
FOR var IN itabIteracion sobre tabla interna
FOR var IN itab WHERE (...)Iteracion filtrada
FOR var IN itab INDEX INTO idxCon indice de fila
FOR i = 1 UNTIL i > nIteracion con contador (ascendente)
FOR i = n THEN i - 1 UNTIL i < 1Iteracion con contador (descendente)
FOR i = 1 WHILE condIteracion condicional
FOR GROUPS grp OF var IN itab GROUP BY ...Agrupacion

Combinaciones

" FOR en VALUE
DATA(lt_result) = VALUE ty_tab( FOR ... ( ... ) ).
" FOR en REDUCE
DATA(lv_result) = REDUCE type( INIT ... FOR ... NEXT ... ).
" FOR en NEW
DATA(lr_result) = NEW ty_tab( FOR ... ( ... ) ).
" FOR anidado
DATA(lt_matrix) = VALUE ty_tab(
FOR i = 1 UNTIL i > 3
FOR j = 1 UNTIL j > 3
( row = i col = j )
).

Notas importantes / Mejores practicas

  • FOR solo se puede usar dentro de VALUE, REDUCE, NEW - no de forma independiente.
  • WHERE filtra de forma mas eficiente que un IF posterior en el cuerpo.
  • INDEX INTO proporciona el indice de fila actual (basado en 1).
  • FOR anidado crea productos cartesianos - cuidado con tablas grandes!
  • FOR GROUPS es potente para agregaciones por campos de agrupacion.
  • LET permite variables auxiliares locales dentro de la expresion FOR.
  • BASE conserva entradas de tabla existentes al construir.
  • Combinar con CORRESPONDING para transformaciones de estructura.
  • FOR ... UNTIL/WHILE para iteraciones sin tabla fuente (series numericas, etc.).
  • Con logica compleja un LOOP AT puede ser mas legible.