Modern Internal Table Operations

FILTER requires a sorted or hashed key (primary or secondary) on the filtered table (or the filter table):

11.1 VALUE

DATA(lt_orders) = VALUE tt_orders(
  ( order_id = '1' customer = 'C1' amount = 100 status = 'O' )
  ( order_id = '2' customer = 'C2' amount = 250 status = 'C' )
  ( order_id = '3' customer = 'C1' amount = 75  status = 'O' ) ).

" Default values for all rows
DATA(lt_open) = VALUE tt_orders( status = 'O'
  ( order_id = '4' customer = 'C3' amount = 10 )
  ( order_id = '5' customer = 'C3' amount = 20 ) ).

" Append to existing content
lt_orders = VALUE #( BASE lt_orders ( order_id = '6' customer = 'C4' amount = 5 ) ).

" Include lines of another table
lt_orders = VALUE #( BASE lt_orders ( LINES OF lt_open ) ).

11.2 FOR iteration expressions

" Transform
TYPES tt_ids TYPE STANDARD TABLE OF ty_order-order_id WITH EMPTY KEY.
DATA(lt_ids) = VALUE tt_ids( FOR ls IN lt_orders ( ls-order_id ) ).

" Filter with WHERE
DATA(lt_c1) = VALUE tt_orders( FOR ls IN lt_orders WHERE ( customer = 'C1' ) ( ls ) ).

" With index and LET helper variables
DATA(lt_text) = VALUE string_table(
  FOR ls IN lt_orders INDEX INTO lv_i
  LET lv_prefix = |#{ lv_i }: | IN
  ( |{ lv_prefix }{ ls-customer } { ls-amount }| ) ).

" Nested FOR (cross product)
DATA(lt_pairs) = VALUE string_table(
  FOR a IN lt_colors FOR b IN lt_sizes ( |{ a }-{ b }| ) ).

" Counter-based
DATA(lt_n) = VALUE int4_table( FOR i = 1 THEN i * 2 UNTIL i > 1000 ( i ) ).

11.3 REDUCE

DATA(lv_total) = REDUCE decfloat34( INIT sum = CONV decfloat34( 0 )
                                    FOR ls IN lt_orders
                                    NEXT sum = sum + ls-amount ).

DATA(lv_open_count) = REDUCE i( INIT n = 0
                                FOR ls IN lt_orders WHERE ( status = 'O' )
                                NEXT n = n + 1 ).

DATA(lv_csv) = REDUCE string( INIT s = ``  sep = ``
                              FOR ls IN lt_orders
                              NEXT s = s && sep && ls-order_id  sep = `,` ).

11.4 FILTER

FILTER requires a sorted or hashed key (primary or secondary) on the filtered table (or the filter table):

DATA lt_sorted TYPE SORTED TABLE OF ty_order WITH NON-UNIQUE KEY status.
lt_sorted = lt_orders.
DATA(lt_open2)   = FILTER #( lt_sorted WHERE status = 'O' ).
DATA(lt_notopen) = FILTER #( lt_sorted EXCEPT WHERE status = 'O' ).

" Filter by another table
DATA lt_keep TYPE SORTED TABLE OF kunnr WITH UNIQUE KEY table_line.
lt_keep = VALUE #( ( 'C1' ) ( 'C3' ) ).
DATA(lt_sel) = FILTER #( lt_orders_by_cust IN lt_keep WHERE customer = table_line ).

11.5 CORRESPONDING

There is also a lookup-table variant (CORRESPONDING #( itab FROM lookup_tab USING … )) that fills fields from a second table, and the class CL_ABAP_CORRESPONDING for mappings defined at runtime. Classic MOVE-CORRESPONDING a TO b. is still valid (also for tables since 7.40 SP05).

DATA ls_display TYPE zs_order_display.
ls_display = CORRESPONDING #( ls_order ).                         " same-named components
ls_display = CORRESPONDING #( ls_order MAPPING cust_no = customer EXCEPT status ).
ls_display = CORRESPONDING #( BASE ( ls_display ) ls_order ).      " keep non-matched values
DATA(lt_disp) = CORRESPONDING tt_display( lt_orders ).            " whole tables
lt_disp = CORRESPONDING #( lt_orders DISCARDING DUPLICATES ).

11.6 Table comprehensions with conditions

DATA(lt_flagged) = VALUE tt_display(
  FOR ls IN lt_orders
  ( order_id = ls-order_id
    amount   = ls-amount
    flag     = COND #( WHEN ls-amount > 200 THEN 'BIG' ELSE 'SMALL' ) ) ).

11.7 GROUP BY loops (7.40 SP08+)

Unlike AT NEW, grouping does not require pre-sorting and works with any key expression.

" Group by customer and compute totals
LOOP AT lt_orders INTO DATA(ls_ord)
     GROUP BY ( customer = ls_ord-customer
                size     = GROUP SIZE
                index    = GROUP INDEX )
     ASCENDING
     ASSIGNING FIELD-SYMBOL(<group>).

  DATA(lv_sum) = REDUCE decfloat34( INIT s = CONV decfloat34( 0 )
                                    FOR m IN GROUP <group> NEXT s = s + m-amount ).
  out->write( |{ <group>-customer }: { <group>-size } orders, total { lv_sum }| ).

  LOOP AT GROUP <group> INTO DATA(ls_member).
    " each member row
  ENDLOOP.
ENDLOOP.

" Groups as an expression
DATA(lt_customers) = VALUE string_table(
  FOR GROUPS grp OF ls IN lt_orders GROUP BY ls-customer ( CONV string( grp ) ) ).

11.8 Classic vs modern equivalents

READ TABLE t INTO w WITH KEY k = v. w = t[ k = v ]. READ … TRANSPORTING NO FIELDS + sy-subrc line_exists( t[ k = v ]) APPEND in loops VALUE #( ( …) ( …)) LOOP + APPEND VALUE #( FOR …) LOOP + ADD LOOP … WHERE + APPEND FILTER / VALUE #( FOR … WHERE …) CORRESPONDING #() SORT + AT NEW LOOP AT … GROUP BY

11.9 Performance notes

Use hashed tables (or sorted/secondary keys) for repeated key lookups; a linear READ inside a LOOP over a large table is O(n²).

Prefer ASSIGNING / REFERENCE INTO over INTO for wide rows or when modifying.

Avoid DELETE inside loops over the same table on large data; use DELETE … WHERE.

SORT once, then use BINARY SEARCH on standard tables if you can't change the type.