ABAP REDUCE: Aggregating and Accumulating Values

Category
ABAP-Statements
Published
Author
Johannes

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

  1. INIT: Initializes one or more accumulators
  2. FOR: Iterates over a table (or multiple)
  3. NEXT: Updates the accumulators per iteration
  4. 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 LOOP
DATA: lv_sum TYPE i.
LOOP AT lt_numbers INTO DATA(lv_num).
lv_sum = lv_sum + lv_num.
ENDLOOP.
" Modern with REDUCE
DATA(lv_sum2) = REDUCE i(
INIT sum = 0
FOR num IN lt_numbers
NEXT sum = sum + num
).
WRITE: / 'Sum:', lv_sum2. " 150

2. 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 amount
DATA(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.25

3. 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 A
DATA(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. " 2

4. Find Maximum

DATA: lt_values TYPE TABLE OF i.
lt_values = VALUE #( ( 42 ) ( 17 ) ( 99 ) ( 8 ) ( 73 ) ).
" Find maximum
DATA(lv_max) = REDUCE i(
INIT max = 0
FOR val IN lt_values
NEXT max = nmax( val1 = max val2 = val )
).
WRITE: / 'Maximum:', lv_max. " 99

5. 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. " 8

6. Concatenate Strings

DATA: lt_names TYPE TABLE OF string.
lt_names = VALUE #( ( `Anna` ) ( `Bernd` ) ( `Clara` ) ).
" Concatenate names with comma
DATA(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, Clara

7. 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 simultaneously
DATA(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. " 100
WRITE: / 'Count:', ls_stats-count. " 4
WRITE: / 'Average:', lv_average. " 25

8. 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 table
DATA(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, Peter

9. 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 NORTH
DATA(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.00

10. 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 categories
DATA(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. " 150

11. 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 WHILE
DATA(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. " 55

12. 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.00

13. 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 category
DATA(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 parameter
display_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 ... IN iterates over tables, FOR ... UNTIL/WHILE for conditional iteration.
  • WHERE filters the iteration – more efficient than IF in NEXT.
  • LET enables local helper variables within the iteration.
  • Nested FOR loops are possible for multi-dimensional data.
  • BASE in VALUE #() preserves existing table entries when building.
  • Combine with VALUE, COND and FILTER.
  • REDUCE is functional – no side effects on external variables.
  • For complex logic, a LOOP AT may be more readable.