The REDUCE expression enables aggregating and accumulating values through iteration. It is comparable to fold or reduce from functional programming languages and replaces many LOOP constructs with accumulator variables.
Syntax
REDUCE <type>( INIT <accumulator1> = <start_value1> [ <accumulator2> = <start_value2> ... ] FOR <variable> IN <table> [ WHERE ( <condition> ) ] [ FOR <variable2> ... ] NEXT <accumulator1> = <expression1> [ <accumulator2> = <expression2> ... ])Basic Principle
- INIT: Initializes one or more accumulators
- FOR: Iterates over a table (or multiple)
- NEXT: Updates the accumulators per iteration
- The result is the value of the first accumulator after the last iteration
Examples
1. Calculate Simple Sum
DATA: lt_numbers TYPE TABLE OF i.
lt_numbers = VALUE #( ( 10 ) ( 20 ) ( 30 ) ( 40 ) ( 50 ) ).
" Classic with LOOPDATA: lv_sum TYPE i.LOOP AT lt_numbers INTO DATA(lv_num). lv_sum = lv_sum + lv_num.ENDLOOP.
" Modern with REDUCEDATA(lv_sum2) = REDUCE i( INIT sum = 0 FOR num IN lt_numbers NEXT sum = sum + num).
WRITE: / 'Sum:', lv_sum2. " 1502. Sum from Structure Table
TYPES: BEGIN OF ty_order, order_id TYPE i, amount TYPE p DECIMALS 2, END OF ty_order.
DATA: lt_orders TYPE TABLE OF ty_order.
lt_orders = VALUE #( ( order_id = 1 amount = '100.50' ) ( order_id = 2 amount = '200.00' ) ( order_id = 3 amount = '150.75' )).
" Calculate total amountDATA(lv_total) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_order IN lt_orders NEXT total = total + ls_order-amount).
WRITE: / 'Total amount:', lv_total. " 451.253. Count with WHERE
TYPES: BEGIN OF ty_product, id TYPE i, category TYPE string, active TYPE abap_bool, END OF ty_product.
DATA: lt_products TYPE TABLE OF ty_product.
lt_products = VALUE #( ( id = 1 category = 'A' active = abap_true ) ( id = 2 category = 'B' active = abap_false ) ( id = 3 category = 'A' active = abap_true ) ( id = 4 category = 'A' active = abap_false )).
" Count active products in category ADATA(lv_count) = REDUCE i( INIT count = 0 FOR ls_prod IN lt_products WHERE ( category = 'A' AND active = abap_true ) NEXT count = count + 1).
WRITE: / 'Count:', lv_count. " 24. Find Maximum
DATA: lt_values TYPE TABLE OF i.
lt_values = VALUE #( ( 42 ) ( 17 ) ( 99 ) ( 8 ) ( 73 ) ).
" Find maximumDATA(lv_max) = REDUCE i( INIT max = 0 FOR val IN lt_values NEXT max = nmax( val1 = max val2 = val )).
WRITE: / 'Maximum:', lv_max. " 995. Find Minimum
" Find minimum (with first value as start value)DATA(lv_min) = REDUCE i( INIT min = lt_values[ 1 ] FOR val IN lt_values NEXT min = nmin( val1 = min val2 = val )).
WRITE: / 'Minimum:', lv_min. " 86. Concatenate Strings
DATA: lt_names TYPE TABLE OF string.
lt_names = VALUE #( ( `Anna` ) ( `Bernd` ) ( `Clara` ) ).
" Concatenate names with commaDATA(lv_concat) = REDUCE string( INIT result = `` FOR name IN lt_names NEXT result = COND #( WHEN result IS INITIAL THEN name ELSE result && `, ` && name )).
WRITE: / lv_concat. " Anna, Bernd, Clara7. Multiple Accumulators
TYPES: BEGIN OF ty_stats, sum TYPE i, count TYPE i, END OF ty_stats.
DATA: lt_numbers TYPE TABLE OF i.
lt_numbers = VALUE #( ( 10 ) ( 20 ) ( 30 ) ( 40 ) ).
" Calculate sum and count simultaneouslyDATA(ls_stats) = REDUCE ty_stats( INIT sum = 0 count = 0 FOR num IN lt_numbers NEXT sum = sum + num count = count + 1).
DATA(lv_average) = ls_stats-sum / ls_stats-count.
WRITE: / 'Sum:', ls_stats-sum. " 100WRITE: / 'Count:', ls_stats-count. " 4WRITE: / 'Average:', lv_average. " 258. Build Table from Table
TYPES: ty_names TYPE TABLE OF string WITH EMPTY KEY.
DATA: lt_persons TYPE TABLE OF ty_person.
lt_persons = VALUE #( ( name = 'Max' age = 30 ) ( name = 'Anna' age = 25 ) ( name = 'Peter' age = 35 )).
" Extract names to new tableDATA(lt_names) = REDUCE ty_names( INIT names = VALUE ty_names( ) FOR ls_person IN lt_persons NEXT names = VALUE #( BASE names ( ls_person-name ) )).
" Result: Max, Anna, Peter9. Filter and Aggregate Combined
TYPES: BEGIN OF ty_sale, region TYPE string, amount TYPE p DECIMALS 2, END OF ty_sale.
DATA: lt_sales TYPE TABLE OF ty_sale.
lt_sales = VALUE #( ( region = 'NORTH' amount = '1000.00' ) ( region = 'SOUTH' amount = '2000.00' ) ( region = 'NORTH' amount = '1500.00' ) ( region = 'EAST' amount = '800.00' )).
" Sum only for region NORTHDATA(lv_north_total) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_sale IN lt_sales WHERE ( region = 'NORTH' ) NEXT total = total + ls_sale-amount).
WRITE: / 'NORTH Sales:', lv_north_total. " 2500.0010. Nested Iteration (Two FOR)
TYPES: BEGIN OF ty_category, name TYPE string, items TYPE TABLE OF i WITH EMPTY KEY, END OF ty_category.
DATA: lt_categories TYPE TABLE OF ty_category.
lt_categories = VALUE #( ( name = 'A' items = VALUE #( ( 10 ) ( 20 ) ) ) ( name = 'B' items = VALUE #( ( 30 ) ( 40 ) ( 50 ) ) )).
" Sum of all items across all categoriesDATA(lv_total_all) = REDUCE i( INIT total = 0 FOR ls_cat IN lt_categories FOR lv_item IN ls_cat-items NEXT total = total + lv_item).
WRITE: / 'Total sum:', lv_total_all. " 15011. FOR with UNTIL/WHILE
" Iteration with condition (not over table)DATA(lv_factorial) = REDUCE i( INIT fact = 1 n = 1 UNTIL n > 5 NEXT fact = fact * n n = n + 1).
WRITE: / '5! =', lv_factorial. " 120
" With WHILEDATA(lv_sum_while) = REDUCE i( INIT sum = 0 i = 1 WHILE i <= 10 NEXT sum = sum + i i = i + 1).
WRITE: / 'Sum 1-10:', lv_sum_while. " 5512. LET for Local Helper Variables
TYPES: BEGIN OF ty_item, quantity TYPE i, price TYPE p DECIMALS 2, END OF ty_item.
DATA: lt_items TYPE TABLE OF ty_item.
lt_items = VALUE #( ( quantity = 5 price = '10.00' ) ( quantity = 3 price = '25.00' ) ( quantity = 10 price = '5.00' )).
" Calculate total value (quantity * price)DATA(lv_total_value) = REDUCE p DECIMALS 2( INIT total = CONV p DECIMALS 2( 0 ) FOR ls_item IN lt_items LET line_total = ls_item-quantity * ls_item-price IN NEXT total = total + line_total).
WRITE: / 'Total value:', lv_total_value. " 175.0013. Grouping with REDUCE
TYPES: BEGIN OF ty_group_result, category TYPE string, sum TYPE i, END OF ty_group_result, ty_group_results TYPE TABLE OF ty_group_result WITH EMPTY KEY.
DATA: lt_items TYPE TABLE OF ty_product.
lt_items = VALUE #( ( id = 1 category = 'A' active = abap_true ) ( id = 2 category = 'B' active = abap_true ) ( id = 3 category = 'A' active = abap_true ) ( id = 4 category = 'A' active = abap_false ) ( id = 5 category = 'B' active = abap_true )).
" Count per categoryDATA(lt_by_category) = REDUCE ty_group_results( INIT result = VALUE ty_group_results( ) FOR ls_item IN lt_items FOR GROUPS <group> OF <item> IN lt_items GROUP BY <item>-category NEXT result = VALUE #( BASE result ( category = <group> sum = REDUCE i( INIT cnt = 0 FOR m IN GROUP <group> NEXT cnt = cnt + 1 ) ) )).14. Boolean Aggregation (ANY/ALL)
" Check if AT LEAST ONE element meets the condition (ANY)DATA(lv_any_active) = REDUCE abap_bool( INIT any = abap_false FOR ls_prod IN lt_products NEXT any = xsdbool( any = abap_true OR ls_prod-active = abap_true )).
" Check if ALL elements meet the condition (ALL)DATA(lv_all_active) = REDUCE abap_bool( INIT all = abap_true FOR ls_prod IN lt_products NEXT all = xsdbool( all = abap_true AND ls_prod-active = abap_true )).
IF lv_any_active = abap_true. WRITE: / 'At least one product is active'.ENDIF.
IF lv_all_active = abap_true. WRITE: / 'All products are active'.ELSE. WRITE: / 'Not all products are active'.ENDIF.15. REDUCE in Method Calls
METHODS: display_total IMPORTING iv_total TYPE p.
" Directly as parameterdisplay_total( iv_total = REDUCE p DECIMALS 2( INIT sum = CONV p DECIMALS 2( 0 ) FOR ls_order IN lt_orders NEXT sum = sum + ls_order-amount )).Comparison: REDUCE vs. LOOP
" === CLASSIC WITH LOOP ===DATA: lv_sum TYPE i, lv_count TYPE i, lv_max TYPE i.
LOOP AT lt_numbers INTO DATA(lv_num). lv_sum = lv_sum + lv_num. lv_count = lv_count + 1. IF lv_num > lv_max. lv_max = lv_num. ENDIF.ENDLOOP.
" === MODERN WITH REDUCE ===DATA(ls_result) = REDUCE ty_result( INIT sum = 0 count = 0 max = 0 FOR num IN lt_numbers NEXT sum = sum + num count = count + 1 max = nmax( val1 = max val2 = num )).Important Notes / Best Practice
- The first accumulator determines the return type of
REDUCE. - For multiple results: Use a structure accumulator or multiple accumulators.
FOR ... INiterates over tables,FOR ... UNTIL/WHILEfor conditional iteration.WHEREfilters the iteration – more efficient thanIFinNEXT.LETenables local helper variables within the iteration.- Nested
FORloops are possible for multi-dimensional data. BASEinVALUE #()preserves existing table entries when building.- Combine with
VALUE,CONDandFILTER. REDUCEis functional – no side effects on external variables.- For complex logic, a
LOOP ATmay be more readable.