ABAP FOR : Iteration inline dans les expressions

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

L’expression FOR permet l’iteration inline dans les expressions constructeur comme VALUE, REDUCE et NEW. Elle remplace de nombreuses constructions LOOP AT ... APPEND par une syntaxe compacte et fonctionnelle.

Syntaxe

1. FOR … IN (Iteration sur table)

FOR <variable> IN <table> [ INDEX INTO <index> ] [ WHERE ( <condition> ) ]

2. FOR … UNTIL/WHILE (Iteration conditionnelle)

FOR <variable> = <valeur_initiale> [ THEN <expression> ] UNTIL <condition>
FOR <variable> = <valeur_initiale> [ THEN <expression> ] WHILE <condition>

3. FOR GROUPS (Regroupement)

FOR GROUPS <groupe> OF <variable> IN <table>
GROUP BY <expression_regroupement>

Principe de base

FOR est utilise dans VALUE, REDUCE ou NEW :

" Creer une table a partir d'une autre table
DATA(lt_result) = VALUE ty_result_tab(
FOR ls_source IN lt_source
( champ1 = ls_source-champ1 champ2 = ls_source-champ2 )
).

Exemples

1. Transformation de table simple

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 = 'Max' age = 30 )
( id = 2 name = 'Anna' age = 25 )
( id = 3 name = 'Peter' age = 35 )
).
" Extraire uniquement les noms
DATA(lt_names) = VALUE ty_names(
FOR ls_person IN lt_persons
( ls_person-name )
).
" Resultat : Max, Anna, Peter

2. Comparaison : FOR vs. LOOP

" === CLASSIQUE AVEC 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.
" === MODERNE AVEC FOR ===
DATA(lt_names_modern) = VALUE ty_names(
FOR ls_p IN lt_persons
( ls_p-name )
).

3. FOR avec WHERE (Filtrage)

" Uniquement les adultes (age >= 18)
DATA(lt_adults) = VALUE ty_persons(
FOR ls_person IN lt_persons
WHERE ( age >= 18 )
( ls_person )
).
" Condition plus complexe
DATA(lt_filtered) = VALUE ty_persons(
FOR ls_person IN lt_persons
WHERE ( age >= 25 AND name <> 'Max' )
( ls_person )
).

4. Transformation avec calcul

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 )
).
" Calculer les lignes de commande avec prix 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 - Lire l’index de ligne

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 )
).
" Resultat :
" position = 1, name = 'Max"
" position = 2, name = 'Anna"
" position = 3, name = 'Peter"

6. FOR imbrique (Produit cartesien)

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 #( ( `Rouge` ) ( `Bleu` ) ( `Vert` ) ).
lt_sizes = VALUE #( ( `S` ) ( `M` ) ( `L` ) ).
" Creer toutes les combinaisons
DATA(lt_variants) = VALUE ty_variants(
FOR lv_color IN lt_colors
FOR lv_size IN lt_sizes
( color = lv_color
size = lv_size )
).
" Resultat : 9 combinaisons (3x3)
" Rouge-S, Rouge-M, Rouge-L, Bleu-S, Bleu-M, ...

7. FOR … UNTIL (Iteration conditionnelle)

TYPES: ty_numbers TYPE TABLE OF i WITH EMPTY KEY.
" Generer les nombres 1 a 10
DATA(lt_1_to_10) = VALUE ty_numbers(
FOR i = 1 UNTIL i > 10
( i )
).
" Avec THEN pour le pas
DATA(lt_even) = VALUE ty_numbers(
FOR i = 2 THEN i + 2 UNTIL i > 20
( i )
).
" Resultat : 2, 4, 6, 8, 10, 12, 14, 16, 18, 20

8. FOR … WHILE

" Carres inferieurs a 100
DATA(lt_squares) = VALUE ty_numbers(
FOR n = 1 WHILE n * n < 100
( n * n )
).
" Resultat : 1, 4, 9, 16, 25, 36, 49, 64, 81

9. FOR GROUPS - Regroupement

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 = 'NORTH' amount = '1000.00' )
( region = 'SOUTH' amount = '2000.00' )
( region = 'NORTH' amount = '1500.00' )
( region = 'EAST' amount = '800.00' )
( region = 'SOUTH' 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.
" Somme par 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
)
)
).
" Resultat :
" NORTH: 2500.00
" SOUTH: 3200.00
" EAST: 800.00

10. FOR GROUPS avec plusieurs champs de regroupement

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.
" Regroupement par annee et mois
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 pour les 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 dans REDUCE

" Somme de tous les montants
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
).
" Avec filtrage WHERE
DATA(lv_north_total) = REDUCE p DECIMALS 2(
INIT sum = CONV p DECIMALS 2( 0 )
FOR ls_sale IN lt_sales
WHERE ( region = 'NORTH' )
NEXT sum = sum + ls_sale-amount
).

13. FOR dans NEW (References de donnees)

" Creer une table comme reference
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. Etendre l’existant (BASE)

DATA: lt_existing TYPE ty_names.
lt_existing = VALUE #( ( `Existing1` ) ( `Existing2` ) ).
" Ajouter de nouvelles entrees
DATA(lt_combined) = VALUE ty_names(
BASE lt_existing
FOR ls_person IN lt_persons
( ls_person-name )
).
" Resultat : Existing1, Existing2, Max, Anna, Peter

15. Transformation complexe avec 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 = 'Mueller GmbH' ort01 = 'Berlin' )
( kunnr = '2000' name1 = 'Schmidt AG' ort01 = 'Munich' )
).
" Transformation avec 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
)
)
).

Apercu des variantes FOR

VarianteUtilisation
FOR var IN itabIteration sur table interne
FOR var IN itab WHERE (...)Iteration filtree
FOR var IN itab INDEX INTO idxAvec index de ligne
FOR i = 1 UNTIL i > nIteration compteur (croissant)
FOR i = n THEN i - 1 UNTIL i < 1Iteration compteur (decroissant)
FOR i = 1 WHILE condIteration conditionnelle
FOR GROUPS grp OF var IN itab GROUP BY ...Regroupement

Combinaisons

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

Conseils importants / Bonnes pratiques

  • FOR n’est utilisable que dans VALUE, REDUCE, NEW - pas de maniere autonome.
  • WHERE filtre plus efficacement qu’un IF dans le corps.
  • INDEX INTO fournit l’index de ligne actuel (base 1).
  • Les FOR imbriques creent des produits cartesiens - attention avec les grandes tables !
  • FOR GROUPS est puissant pour les agregations par champs de regroupement.
  • LET permet des variables auxiliaires locales dans l’expression FOR.
  • BASE preserve les entrees de table existantes lors de la construction.
  • Combinez avec CORRESPONDING pour les transformations de structure.
  • FOR ... UNTIL/WHILE pour les iterations sans table source (series de nombres, etc.).
  • Pour une logique complexe, un LOOP AT peut etre plus lisible.