Lesson 3 of 6
Constructor expressions: VALUE, CORRESPONDING, REDUCE, FILTER, COND, SWITCH
11 min
Listen to this lesson
A constructor expression builds a value in one expression: OPERATOR type( ... ), where type is a type name or # for "the type the context expects". They replace sequences of CLEAR, APPEND, MOVE-CORRESPONDING, IF and CASE statements. SAP's constructor expressions cheat sheet lists them all; this lesson covers the ones Contoso's developers use daily, with the classic form each replaces.
| Operator | Builds | Replaces | Contoso example |
|---|---|---|---|
VALUE | Structures and internal tables | CLEAR + field assignments + APPEND | items = VALUE #( ( product = 'CMC-ALT-100' qty = 12 ) ( product = 'CMC-ALT-240' qty = 2 ) ). |
NEW | Object or data reference | CREATE OBJECT / CREATE DATA | DATA(calc) = NEW zcl_cmc_core_deposit( ). |
CONV | A value of a given type | Helper variables for type conversion | DATA(rate) = CONV decfloat34( 45 ). |
CORRESPONDING | Structure or table from another, by name | MOVE-CORRESPONDING | out = CORRESPONDING #( agr MAPPING customer_id = customer ). |
COND | One value from conditions | IF / ELSEIF assigning one variable | COND #( WHEN qty > 100 THEN 'BULK' ELSE 'STD' ) |
SWITCH | One value from a case on one operand | CASE assigning one variable | SWITCH #( status WHEN 'OPEN' THEN 'Active' WHEN 'CLOSED' THEN 'Ended' ) |
REDUCE | One value from iterating | LOOP that accumulates a total | REDUCE #( INIT s = 0 FOR i IN items NEXT s = s + i-deposit ) |
FILTER | A table of rows matching a key | LOOP + IF + APPEND | FILTER #( agreements USING KEY by_status WHERE status = 'OPEN' ) |
REF | A data reference | GET REFERENCE OF (rejected in ABAP Cloud) | DATA(r) = REF #( total ). |
The constructor operators covered in this lesson, with the classic statements they replace.
VALUE and CORRESPONDING
VALUE builds a structure or a table in one statement; each inner pair of parentheses is a row. BASE keeps the existing rows: items = VALUE #( BASE items ( product = ... ) ) appends, while leaving out BASE replaces the table. CORRESPONDING copies components with the same names; MAPPING maps different names and EXCEPT leaves components out. One trap: without BASE, components of the target that have no source are initialized, unlike MOVE-CORRESPONDING, which leaves them untouched.
NEW and CONV are the simplest operators and the ones classic developers adopt first. NEW zcl_cmc_core_deposit( ) replaces CREATE OBJECT and can be used directly as an argument. CONV converts a value to a named type in place, which removes the helper variables classic code declared only to change a type before a method call, for example calc->set_rate( CONV #( rate_text ) ). Use CONV deliberately: a conversion that can fail (text to number) still raises its exception, it just happens inside the expression.
REDUCE and FILTER
REDUCE iterates and accumulates: Contoso's total deposit for an agreement is REDUCE decfloat34( INIT sum = 0 FOR item IN items NEXT sum = sum + item-quantity * item-deposit_per_unit ). For 12 units of CMC-ALT-100 at 45.00 and 2 units at 25.00, it returns 590.00. FILTER returns the rows that match a condition on a key: the table needs a sorted or hashed key (primary or secondary) on the filtered components. FILTER ... EXCEPT returns the rows that do not match. For conditions that cannot use a key, use VALUE #( FOR ... WHERE ( ... ) ( ... ) ) instead.
- ClassicCLEAR lv_sum. LOOP AT lt_items INTO ls_item. lv_sum = lv_sum + ls_item-qty * ls_item-rate. ENDLOOP.
- Test firstPin the result: 590.00 for the two-item agreement; 0 for no items
- ModernDATA(sum) = REDUCE decfloat34( INIT s = 0 FOR i IN items NEXT s = s + i-qty * i-rate ).
- Check typeThe INIT type decides the result type; decfloat34 avoids integer truncation
- Re-runBoth tests green; review for readability
COND and SWITCH
COND returns a value from the first true WHEN; SWITCH compares one operand against constant values. Both may raise exceptions with THEN THROW. If no branch matches and there is no ELSE, the result is initial, which is a quiet source of defects: a status text that is blank for a new status nobody anticipated. Always write an ELSE, even if it throws.
- Building a structure or table from literals or fields?Yes → VALUE (with BASE to keep rows) · No → next question
- Copying fields between structures or tables?Yes → CORRESPONDING (MAPPING, EXCEPT, BASE) · No → next question
- Reducing many rows to one value?Yes → REDUCE with an explicit INIT type · No → next question
- Selecting rows by a key condition?Yes → FILTER (needs a sorted or hashed key) · No → VALUE #( FOR ... WHERE ... )
- Choosing one value by conditions?Yes → COND, or SWITCH for one operand; always with ELSE
What goes wrong in testing
- REDUCE with an integer INIT.
INIT s = 0with#can produce an integer sum and drop decimals. Name the type. - CORRESPONDING wipes fields. Target fields with no source are initialized. Tests that check only the copied fields miss it.
- VALUE without BASE. Meant to append, but replaces the table.
- FILTER on a table without a suitable key. A syntax error, so the developer switches to a loop; better to define a secondary key.
- COND without ELSE. A new status value produces a blank text.
What would you do?
After Kenji refactored a MOVE-CORRESPONDING into CORRESPONDING #( ), Dan notices that the total deposit field in a report output is now always 0.00. What happened?
- Constructor expressions build values in one expression; # means the type the context expects.
- VALUE replaces a table unless you use BASE; CORRESPONDING initializes target fields with no source.
- REDUCE needs an explicit INIT type for amounts; FILTER needs a sorted or hashed key.
- COND and SWITCH should always have an ELSE.
Check your understanding · not graded
Lesson check
1. What does # mean in VALUE #( ... )?
2. How do you append a row with VALUE without losing existing rows?
3. What does FILTER require of the internal table?
4. Why name the type in REDUCE decfloat34( INIT s = 0 ... )?
5. What does COND return when no WHEN matches and there is no ELSE?