Processing the Contents of Internal Tables

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Process the contents of an internal table

Sorted Internal Tables

In a standard internal table, the contents are unsorted. New entries that you add to the table using the APPEND statement are added to the end of the table.

Sorting a Standard Internal Table

In this video, you will see how you can sort a table by its key, by any field, and in an ascending and descending order.

Try It Out: Sorted Internal Tables

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):
    Code snippet
    
    TYPES: t_flights TYPE STANDARD TABLE OF /dmo/flight WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
    DATA: flights TYPE t_flights.
    
    
    flights = VALUE #( ( client = sy-mandt carrier_id = 'LH' connection_id = '0400' flight_date = '20230201' plane_type_id = '747-400' price = '600' currency_code = 'EUR' )
    ( client = sy-mandt carrier_id = 'LH' connection_id = '0400' flight_date = '20230115' plane_type_id = '747-400' price = '600' currency_code = 'EUR' )
    ( client = sy-mandt carrier_id = 'QF' connection_id = '0006' flight_date = '20230112' plane_type_id = 'A380' price = '1600' currency_code = 'AUD' )
    ( client = sy-mandt carrier_id = 'AA' connection_id = '0017' flight_date = '20230110' plane_type_id = '747-400' price = '600' currency_code = 'USD' )
    ( client = sy-mandt carrier_id = 'UA' connection_id = '0900' flight_date = '20230201' plane_type_id = '777-200' price = '600' currency_code = 'USD' ) ).
    
    
    out->write( 'Contents Before Sort' ).
    out->write( '____________________' ).
    out->write( flights ).
    out->write( ` ` ).
    
    
    * Sort with no additions - sort by primary table key carrier_id connection_id flight_date
    SORT flights.
    
    
    out->write( 'Effect of SORT with no additions - sort by primary table key' ).
    out->write( '____________________________________________________________' ).
    out->write( flights ).
    out->write( ` ` ).
    
    
    * Sort with field list - default sort direction is ascending
    SORT flights BY currency_code plane_type_id.
    out->write( 'Effect of SORT with field list - ascending is default direction' ).
    out->write( '________________________________________________________________' ).
    out->write( flights ).
    out->write( ` ` ).
    
    
    * Sort with field list and sort directions.
    SORT flights BY carrier_Id ASCENDING flight_Date DESCENDING.
    out->write( 'Effect of SORT with field list and sort direction' ).
    out->write( '_________________________________________________' ).
    out->write( flights ).
    out->write( ` ` ).
    Expand
  3. Select CTRL + F3 to activate the class and F9 to execute it as a console app.
  4. Play around with the source code to get familiar with the concepts.

Duplicate Record Deletion

Sometimes you will need to delete duplicate entries from an internal table. For instance, in this example, each combination of carrier_id and connection_id should only appear once in the table.

You can use the statement DELETE ADJACENT DUPLICATES to remove duplicate entries from an internal table. However, the statement only works if the entries are adjacent, that is, next to each other.

Sort the Internal Table First

Before using the DELETE ADJACENT DUPLICATES statement, you must sort the internal table according to the columns that you want to screen for duplicate values – in this case, xz carrier_id and connection_id.

However, even though the duplicate flight numbers are now next to each other, the DELETE ADJACENT FIELDS statement in this form does not delete anything. This is because the statement in its basic form considers all key fields of the table when identifying duplicate entries, and since the two LH 0400 connections have different values of flight_date, they do not count as duplicate entries.

In order to delete entries that have the same combination of carrier_id and connection_id, even if they have different values of flight_date, you must use the COMPARING addition of the DELETE ADJACENT DUPLICATES statement. By using COMPARING carrier_id connection_id in the statement, you tell the system only to consider these fields when identifying duplicate entries.

You may specify any combination of key and non-key fields in the COMPARING addition, but you must first have sorted the table according to the same fields.

There is also an addition COMPARING ALL FIELDS. If you use this, the entire table line of two entries must be identical in order for the system to identify a duplicate entry.

Try It Out: Duplicate Record Deletion

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( ):
    Code snippet
    
    TYPES t_flights TYPE STANDARD TABLE OF /dmo/flight WITH NON-UNIQUE KEY carrier_id connection_id flight_date.
    DATA: flights TYPE t_flights.
    
    
    flights = VALUE #( ( client = sy-mandt carrier_id = 'LH' connection_id = '0400' flight_date = '20230201' plane_type_id = '747-400' price = '600' currency_code = 'EUR' )
    ( client = sy-mandt carrier_id = 'QF' connection_id = '0006' flight_date = '20230112' plane_type_id = 'A380' price = '1600' currency_code = 'AUD' )
    ( client = sy-mandt carrier_id = 'AA' connection_id = '0017' flight_date = '20230110' plane_type_id = '747-400' price = '600' currency_code = 'USD' )
    ( client = sy-mandt carrier_id = 'LH' connection_id = '0400' flight_date = '20230301' plane_type_id = '747-400' price = '600' currency_code = 'EUR' )
    ( client = sy-mandt carrier_id = 'UA' connection_id = '0900' flight_date = '20230201' plane_type_id = '777-200' price = '600' currency_code = 'USD' )
    ( client = sy-mandt carrier_id = 'QF' connection_id = '0006' flight_date = '20230210' plane_type_id = 'A380' price = '1600' currency_code = 'AUD' ) ).
    
    
    out->write( 'Contents Before DELETE ADJACENT DUPLICATES' ).
    out->write( '____________________' ).
    out->write( flights ).
    out->write( ` ` ).
    
    
    DELETE ADJACENT DUPLICATES FROM flights.
    out->write( 'Contents after DELETE ADJACENT DUPLICATES' ).
    out->write( 'Nothing deleted - key values are not adjacent' ).
    out->write( 'Sort the table before DELETE ADJACENT DUPLICATES').
    out->write( flights ).
    out->write( ` ` ).
    
    
    SORT flights BY carrier_id connection_id flight_date.
    DELETE ADJACENT DUPLICATES FROM flights.
    out->write( 'Contents after DELETE ADJACENT DUPLICATES' ).
    out->write( 'Nothing deleted - ABAP compares all key values including flight_date, which is different for every entry' ).
    out->write( flights ).
    out->write( ` ` ).
    
    
    DELETE ADJACENT DUPLICATES FROM flights COMPARING carrier_id connection_id.
    out->write( 'Contents after DELETE ADJACENT DUPLICATES with COMPARING and field list' ).
    out->write( 'Entries with identical values of carrier_id and connection_id have been deleted' ).
    out->write( flights ).
    Expand
  3. Select CTRL + F3 to activate the class and F9 to execute it as a console app.
  4. Play around with the source code to get familiar with the concepts.

Table Comprehensions

You will often need to fill an internal table with values that you have read from another internal table. Sometimes it is a simple matter of copying data from one table into another. Other times, you will need to read data from more than one source table to fill your target table and maybe use expressions along the way to calculate values.

ABAP provides means for performing these tasks. In the case of simply copying data from one table to another, you can use the CORRESPONDING #( ) expression. For more complex operations, you can use a table comprehension.

To copy data between identically-named fields of two internal tables, use the CORRESPONDING operator. This works similarly to CORRESPONDING for structures – for each row of the source internal table, the system creates a new row in the target internal table and copies data between identically-named fields. Source fields for which there is no identically named field in the target are not copied. Target fields for which there is no identically named field in the source are filled with type-specific initial values.

In the example, the source internal table carriers contains three rows. Therefore, after the value assignment, the target internal table connections also contains three rows.

Fields carrier_id and carrier_name exist in both internal tables. They are copied from source to target. Field currency_code only exists in the source. It is not copied. Fields connection_id, airport_from_id, and airport_to_id exist only in the target. They are filled with initial values.

Note:

If the target internal table contains data before the assignment, the system deletes it.

Let us now consider a more complicated example. In a program, you have an internal table containing flight connections and another containing the names and locations of airports. You need to fill a third internal table with the carrier_id, connection_id and airport_from_id of the connections table and the airport name from the airports table. You can do this using a table comprehension.

To fill one internal table from another, you can use the VALUEoperator with a FOR expression. FOR implements a loop over the source table and places each row sequentially into the variable line. In the parentheses, you then assign values to the components of the new row using the syntax component = VALUE. This is exactly the same syntax as you use when you fill a structure using VALUE #( ).

Here is the code from our example. FOR line IN connections iterates over the connections table and places each line sequentially into the variable line. In the parentheses, we construct each new line of the table result_table. On the left-hand side of the expressions are the columns of result_table, on the right-hand side, the values that we want to assign to them. The columns carrier_id, connection_id and departure_airport are filled using the components carrier_id, connection_id, and airport_from_id of line respectively. To fill the column departure_airport_name, we use a table expression to look up the name of the airport in the internal table airports using the component airport_from_id of line as the airport key.

You can restrict the iteration over the source table either by using WHERE and specifying a logical condition (for example, only the connections for a particular airline) or by specifying a range of lines using the additions FROM and TO.

Try It Out: Table Comprehensions

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( )
    Code snippet
    
    TYPES: BEGIN OF t_connection,
    carrier_id TYPE /dmo/carrier_id,
    connection_id TYPE /dmo/connection_id,
    departure_airport TYPE /dmo/airport_from_id,
    departure_airport_Name TYPE /dmo/airport_Name,
    END OF t_connection.
    
    
    TYPES t_connections TYPE STANDARD TABLE OF t_connection WITH NON-UNIQUE KEY carrier_id connection_id.
    
    
    DATA connections TYPE TABLE OF /dmo/connection.
    DATA airports TYPE TABLE OF /dmo/airport.
    DATA result_table TYPE t_connections.
    
    
    * Aim of the method:
    * Read a list of connections from the database and use them to fill an internal table result_table.
    * This contains some data from the table connections and adds the name of the departure airport.
    
    
    SELECT FROM /dmo/airport FIELDS * INTO TABLE @airports.
    SELECT FROM /dmo/connection FIELDS * INTO TABLE @connections.
    
    
    
    
    out->write( 'Connection Table' ).
    out->write( '________________' ).
    out->write( connections ).
    out->write( ` ` ).
    
    
    
    
    * The VALUE expression iterates over the table connections. In each iteration, the variable line
    * accesses the current line. Inside the parentheses, we build the next line of result_table by
    * copying the values of line-carrier_Id, line-connection_Id and line-airport_from_id, then
    * loooking up the airport name in the internal table airports using a table expression
    
    
    result_table = VALUE #( FOR line IN connections ( carrier_Id = line-carrier_id
    connection_id = line-connection_id
    departure_airport = line-airport_from_id
    departure_airport_name = airports[ airport_id = line-airport_from_id ]-name ) ).
    
    
    out->write( 'Results' ).
    out->write( '_______' ).
    out->write( result_table ).
    
    
    Expand
  3. Select CTRL + F3 to activate the class and F9 to execute it as a console app.
  4. Play around with the source code to get familiar with the concepts.

Table Reductions

Sometimes when you iterate over an internal table, you need to summarize information – to produce a sum or row count, for example.

The simplest form of reduction iterates over an internal table and returns an elementary value. The INIT part of the reduction defines a variable total, whose type must be compatible with the type of sum. FOR line IN flights is an iteration over the internal table flights in which each line is placed sequentially into the work area line. In the NEXT expression, the contents of seats_occupied from each line are added to the variable total. At the end of the iteration, the contents of total are assigned to the variable sum.

The return value of a reduction can also have a structured type. In this example, the type t_results is a structure containing the components occupied and maximum. In the INIT section of the reduction, we declare a variable totals with the type t_results. In the NEXT expression, we address the components occupied and maximum, compiling the sum of each across the entire internal table.

The type of the target variable and the type of the result of the REDUCE expression must be compatible.

As well as having a return value, a REDUCE expression can also use local variables. In this example, the INIT section of the expression contains two data declarations – totals_avg, which will be the return value, and count. As count is declared with the integer value 1, the system assigns it the type I. We then use count in the NEXT section of the expression. In each iteration, we use it to calculate the average occupation of all flights so far, adding 1 to the value at the end of each iteration.

Try It Out: Table Reductions

  1. Create a new class that implements the interface IF_OO_ADT_CLASSRUN.
  2. Copy the following code snippet to the implementation part of method if_oo_adt_classrun~main( )
    Code snippet
    
    TYPES: BEGIN OF t_results,
    occupied TYPE /dmo/plane_seats_occupied,
    maximum TYPE /dmo/plane_seats_max,
    END OF t_results.
    
    
    TYPES: BEGIN OF t_results_with_Avg,
    occupied TYPE /dmo/plane_seats_occupied,
    maximum TYPE /dmo/plane_seats_max,
    average TYPE p LENGTH 16 DECIMALS 2,
    END OF t_results_with_avg.
    
    
    DATA flights TYPE TABLE OF /dmo/flight.
    SELECT FROM /dmo/flight FIELDS * INTO TABLE @flights.
    
    
    * Result is a scalar data type
    DATA(sum) = REDUCE i( INIT total = 0 FOR line IN flights NEXT total += line-seats_occupied ).
    out->write( 'Result is a scalar data type' ).
    out->write( '_____________ ______________' ).
    out->write( sum ).
    out->write( ` ` ).
    
    
    * Result is a structured data type
    DATA(results) = REDUCE t_results( INIT totals TYPE t_results
    FOR line IN flights NEXT totals-occupied += line-seats_occupied
    totals-maximum += line-seats_max ).
    out->write( 'Result is a structure' ).
    out->write( '_____________________' ).
    
    
    out->write( results ).
    out->write( ` ` ).
    
    
    * Result is a structured data type
    * Reduction uses a local helper variable
    * Result of the reduction is always the *first* variable declared after init
    out->write( 'Result is a structure. The average is calculated using a local helper variable' ).
    out->write( '______________________________________________________________________________' ).
    
    
    DATA(result_with_Average) = REDUCE t_results_with_avg( INIT totals_avg TYPE t_results_with_avg count = 1
    FOR line IN flights NEXT totals_Avg-occupied += line-seats_occupied
    totals_avg-maximum += line-seats_max
    totals_avg-average = totals_avg-occupied / count
    count += 1 ).
    out->write( result_with_average ).
    
    
    Expand
  3. Select CTRL + F3 to activate the class and F9 to execute it as a console app.
  4. Play around with the source code to get familiar with the concepts.

Log in to track your progress & complete quizzes