Working with Internal Tables — The Deep Dive
Advanced internal table operations: MODIFY, DELETE, SORT, COLLECT, CORRESPONDING, FILTER, and FOR expressions in ABAP.
Working with Internal Tables — The Deep Dive
What You'll Learn
- Modifying rows in place
- Deleting rows by key, index, and condition
- Sorting internal tables
- COLLECT for aggregation
- CORRESPONDING for mapping between structures
- FOR expressions and FILTER (modern ABAP)
Setup
REPORT z_itab_deep_dive.
TYPES: BEGIN OF ty_employee,
name TYPE string,
department TYPE string,
salary TYPE i,
END OF ty_employee.
DATA: lt_employees TYPE TABLE OF ty_employee.
lt_employees = VALUE #(
( name = 'Alice' department = 'Engineering' salary = 120000 )
( name = 'Bob' department = 'Marketing' salary = 95000 )
( name = 'Charlie' department = 'Engineering' salary = 130000 )
( name = 'Diana' department = 'Sales' salary = 88000 )
( name = 'Eve' department = 'Marketing' salary = 92000 )
).
Modifying Rows
MODIFY — update by index
DATA: ls_employee TYPE ty_employee.
* Read row 2, change salary, write back
READ TABLE lt_employees INTO ls_employee INDEX 2.
ls_employee-salary = 100000.
MODIFY lt_employees FROM ls_employee INDEX 2.
* Verify
READ TABLE lt_employees INTO ls_employee INDEX 2.
WRITE: / |Bob's new salary: { ls_employee-salary }|.
Expected Output
Bob's new salary: 100000
MODIFY — update by condition (WHERE)
* Give all engineers a 10% raise
LOOP AT lt_employees INTO ls_employee WHERE department = 'Engineering'.
ls_employee-salary = ls_employee-salary * 11 / 10.
MODIFY lt_employees FROM ls_employee.
ENDLOOP.
MODIFY with transporting specific fields
* Only update the salary field, leave other fields untouched
ls_employee-salary = 150000.
MODIFY lt_employees FROM ls_employee INDEX 1 TRANSPORTING salary.
Deleting Rows
DELETE by index
DELETE lt_employees INDEX 3. " Remove third row
DELETE by condition (WHERE)
* Remove all employees earning less than 95000
DELETE lt_employees WHERE salary < 95000.
DELETE ADJACENT DUPLICATES
* Remove consecutive duplicate rows (table must be sorted first)
SORT lt_employees BY department.
DELETE ADJACENT DUPLICATES FROM lt_employees COMPARING department.
Sorting
* Sort by salary ascending (default)
SORT lt_employees BY salary.
* Sort by salary descending
SORT lt_employees BY salary DESCENDING.
* Sort by multiple fields
SORT lt_employees BY department ASCENDING salary DESCENDING.
* Verify
LOOP AT lt_employees INTO ls_employee.
WRITE: / |{ ls_employee-department } - { ls_employee-name } - { ls_employee-salary }|.
ENDLOOP.
Expected Output (sorted by dept asc, salary desc)
Engineering - Charlie - 143000
Engineering - Alice - 132000
Marketing - Bob - 100000
Marketing - Eve - 92000
Sales - Diana - 88000
COLLECT — Automatic Aggregation
COLLECT is unique to ABAP — no direct Python or Java equivalent. It aggregates numeric fields automatically:
TYPES: BEGIN OF ty_dept_total,
department TYPE string,
salary TYPE i,
headcount TYPE i,
END OF ty_dept_total.
DATA: lt_totals TYPE TABLE OF ty_dept_total,
ls_total TYPE ty_dept_total.
* Reset and rebuild from original data
lt_employees = VALUE #(
( name = 'Alice' department = 'Engineering' salary = 120000 )
( name = 'Bob' department = 'Marketing' salary = 95000 )
( name = 'Charlie' department = 'Engineering' salary = 130000 )
( name = 'Diana' department = 'Sales' salary = 88000 )
( name = 'Eve' department = 'Marketing' salary = 92000 )
).
LOOP AT lt_employees INTO DATA(ls_emp).
ls_total-department = ls_emp-department.
ls_total-salary = ls_emp-salary.
ls_total-headcount = 1.
COLLECT ls_total INTO lt_totals. " Adds numeric fields for matching keys
ENDLOOP.
LOOP AT lt_totals INTO ls_total.
WRITE: / |{ ls_total-department }: { ls_total-headcount } employees, total salary { ls_total-salary }|.
ENDLOOP.
Expected Output
Engineering: 2 employees, total salary 250000
Marketing: 2 employees, total salary 187000
Sales: 1 employees, total salary 88000
COLLECT looks for a row with matching non-numeric fields (department). If found, it adds the numeric fields (salary, headcount). If not found, it inserts a new row. This is like a GROUP BY SUM() done entirely in memory.
CORRESPONDING — Map Between Structures
When two structures have overlapping field names, CORRESPONDING copies matching fields:
TYPES: BEGIN OF ty_person,
name TYPE string,
age TYPE i,
city TYPE string,
END OF ty_person.
TYPES: BEGIN OF ty_employee_ext,
name TYPE string,
department TYPE string,
city TYPE string,
salary TYPE i,
END OF ty_employee_ext.
DATA: ls_person TYPE ty_person VALUE 'Alice',
ls_emp_ext TYPE ty_employee_ext.
ls_person-name = 'Alice'.
ls_person-age = 30.
ls_person-city = 'Mumbai'.
* Copy matching fields (name, city) — non-matching (age, department, salary) are ignored
ls_emp_ext = CORRESPONDING #( ls_person ).
WRITE: / |Name: { ls_emp_ext-name }, City: { ls_emp_ext-city }, Dept: { ls_emp_ext-department }|.
Expected Output
Name: Alice, City: Mumbai, Dept:
name and city matched and were copied. department has no match in ty_person, so it stays empty. age has no match in ty_employee_ext, so it's ignored.
FOR Expressions (Modern ABAP)
Like Python's list comprehensions:
* Filter — equivalent to Python's [e for e in employees if e.dept == 'Engineering']
DATA(lt_engineers) = VALUE ty_employee_tab(
FOR ls_e IN lt_employees WHERE ( department = 'Engineering' )
( ls_e )
).
* Transform — equivalent to Python's [{"name": e.name} for e in employees]
TYPES: BEGIN OF ty_name_only,
name TYPE string,
END OF ty_name_only.
TYPES: ty_name_tab TYPE TABLE OF ty_name_only WITH EMPTY KEY.
DATA(lt_names) = VALUE ty_name_tab(
FOR ls_e IN lt_employees
( name = ls_e-name )
).
FILTER (ABAP 7.40+)
For SORTED and HASHED tables, FILTER is the fastest way to extract subsets:
DATA: lt_sorted TYPE SORTED TABLE OF ty_employee WITH NON-UNIQUE KEY department.
lt_sorted = lt_employees.
DATA(lt_engineering) = FILTER #( lt_sorted WHERE department = 'Engineering' ).
FILTER uses the table's key for efficient lookup — it's faster than LOOP AT with WHERE on sorted/hashed tables.
Common Mistakes
- Modifying a table while looping over it without care. Deleting rows inside a
LOOP ATchanges the table size, causing skipped rows. UseDELETE WHEREoutside the loop, or loop backward. - Forgetting that SORT modifies the table in place. Unlike Python's
sorted()which returns a new list, ABAP'sSORTchanges the original table. If you need the original order preserved, copy the table first. - Using COLLECT with non-numeric character fields as keys. COLLECT matches rows by comparing all non-numeric fields. If your "key" includes a string that varies slightly (trailing spaces, case differences), you'll get unexpected duplicate rows instead of aggregation.
- Assuming FOR expressions work on STANDARD tables with WHERE. The WHERE clause in FOR works on all table types, but FILTER (more efficient) requires SORTED or HASHED keys.
Key Takeaways
- MODIFY updates rows by index, condition, or work area. Use TRANSPORTING to update specific fields only.
- DELETE removes by index or WHERE condition. DELETE ADJACENT DUPLICATES needs a sorted table.
- SORT modifies the table in place — sort by multiple fields with ASC/DESC.
- COLLECT auto-aggregates numeric fields for matching non-numeric keys — ABAP's in-memory GROUP BY.
- CORRESPONDING copies fields with matching names between structures.
- FOR expressions are ABAP's list comprehensions. FILTER is optimized for sorted/hashed tables.
Next Lesson
Internal tables hold data. But how do you access that data efficiently without copying it? In Lesson 11: Work Areas, Field Symbols, and Data References, we'll learn the three ways to access data in ABAP — and the critical performance differences between them.