Code: Alles auswählen.
SELECT .... field IN @range
Code: Alles auswählen.
CLASS-METHODS condense_range CHANGING ct_range TYPE ANY TABLE .
Code: Alles auswählen.
REPORT.
END-OF-SELECTION.
SELECT 'I' AS sign,
'EQ' AS option,
cdat AS low,
cdat AS high
FROM reposrc
WHERE cnam = 'SAP'
INTO TABLE @DATA(gt_range).
zcl_utilities=>condense_range( CHANGING ct_range = gt_range ).
cl_salv_table=>factory( IMPORTING
r_salv_table = DATA(go_salv) " Basisklasse einfache ALV Tabellen
CHANGING
t_table = gt_range ).
go_salv->display( ).
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
ewx
Jein. Es ist definitiv nicht schön, aber HIGH ist bei EQ nicht verboten sondern wird einfach ingoriert. Denn das Feld ist ja immer in der Range vorhanden, ist aber üblicherweise bei EQ initial gefüllt. Aber der SELECT inkl. HIGH macht es halt so einfach die korrekte Ausgabestruktur zu erzeugen. Die eigentlich saubere Version mittels sql-Funktion CAST ist auf älteren Releases allerdings noch nicht für Datumsfelder möglich, wohingegen das von mir gepostete Konstrukt auf nahezu allen halbwegs modernen Releases lauffähig sein sollte.a-dead-trousers hat geschrieben: ↑20.03.2023 13:14Dein Test-Coding ist falsch. HIGH darf bei EQ nicht befüllt werden.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag:
a-dead-trousers
Code: Alles auswählen.
METHOD condense_range.
FIELD-SYMBOLS <fs_any> TYPE any.
FIELD-SYMBOLS <fsign> TYPE any.
FIELD-SYMBOLS <foption> TYPE any.
FIELD-SYMBOLS <flow> TYPE any.
FIELD-SYMBOLS <fhigh> TYPE any.
FIELD-SYMBOLS <fs> TYPE any.
FIELD-SYMBOLS <fo> TYPE any.
FIELD-SYMBOLS <fl> TYPE any.
FIELD-SYMBOLS <fh> TYPE any.
FIELD-SYMBOLS <ftable> TYPE STANDARD TABLE.
FIELD-SYMBOLS <fline> TYPE any.
DATA ref_line TYPE REF TO data.
DATA ref_table TYPE REF TO data.
FIELD-SYMBOLS <fsrtable> TYPE STANDARD TABLE.
FIELD-SYMBOLS <fsrline> TYPE any.
DATA ref_elem TYPE REF TO cl_abap_elemdescr.
DATA wdfies TYPE dfies.
DATA fbname TYPE progname.
DATA mask TYPE string.
* Check gefüllt
IF ct_range[] IS INITIAL. RETURN. ENDIF.
* Check ist eine Standard Tabelle
ASSIGN ct_range TO <ftable>.
IF sy-subrc NE 0. RETURN. ENDIF.
READ TABLE <ftable> ASSIGNING <fline> INDEX 1.
IF sy-subrc NE 0. RETURN. ENDIF.
* Check gibt es die SIGN/OPTION/LOW/HIGH Felder
ASSIGN COMPONENT 'SIGN' OF STRUCTURE <fline> TO <fsign>.
IF sy-subrc NE 0. RETURN. ENDIF.
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <fline> TO <foption>.
IF sy-subrc NE 0. RETURN. ENDIF.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <fline> TO <flow>.
IF sy-subrc NE 0. RETURN. ENDIF.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <fline> TO <fhigh>.
IF sy-subrc NE 0. RETURN. ENDIF.
TRY.
CREATE DATA ref_line LIKE LINE OF <ftable>.
CATCH cx_root.
RETURN.
ENDTRY.
ASSIGN ref_line->* TO <fsrline>.
ASSIGN COMPONENT 'SIGN' OF STRUCTURE <fsrline> TO <fs>.
IF sy-subrc NE 0. RETURN. ENDIF.
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <fsrline> TO <fo>.
IF sy-subrc NE 0. RETURN. ENDIF.
<fo> = 'BT'.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <fsrline> TO <fl>.
IF sy-subrc NE 0. RETURN. ENDIF.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <fsrline> TO <fh>.
IF sy-subrc NE 0. RETURN. ENDIF.
* Hole Datenelement info
TRY.
ref_elem ?= cl_abap_datadescr=>describe_by_data( p_data = <fl> ).
CATCH cx_root.
RETURN.
ENDTRY.
* Conversion Exit ?
IF ref_elem->is_ddic_type( ) = 'X'.
TRY.
wdfies = ref_elem->get_ddic_field( ).
CATCH cx_root.
CLEAR wdfies.
ENDTRY.
ENDIF.
TRY.
CREATE DATA ref_table LIKE TABLE OF <fline>.
CATCH cx_root.
RETURN.
ENDTRY.
ASSIGN ref_table->* TO <fsrtable>.
SORT ct_range BY ('LOW') ASCENDING.
LOOP AT ct_range ASSIGNING <fline>.
ASSIGN COMPONENT 'SIGN' OF STRUCTURE <fline> TO <fsign>.
IF <fsign> NE 'I'. CONTINUE. ENDIF.
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <fline> TO <foption>.
IF <foption> NE 'EQ'. CONTINUE. ENDIF.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <fline> TO <flow>.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <fline> TO <fhigh>.
clear <fhigh>.
IF <fsrtable> IS NOT INITIAL.
IF <fl> IN <fsrtable>.
DELETE ct_range.
CONTINUE.
ENDIF.
ENDIF.
IF <fs> IS INITIAL.
<fs> = 'I'.
<fl> = <flow>.
<fh> = <flow>.
ENDIF.
IF <flow> BETWEEN <fl> AND <fh>.
DELETE ct_range.
CONTINUE.
ENDIF.
TRY.
<fh> = <fh> + 1.
CATCH cx_root.
CONTINUE.
ENDTRY.
CASE wdfies-convexit.
WHEN ' '.
WHEN 'ALPHA'. <fh> = |{ <fh> ALPHA = IN }|.
WHEN OTHERS.
* FÜR MATNR funktioniert es (bei uns) - aber sicher nicht überall
SHIFT <fh> LEFT DELETING LEADING ' 0'.
TRY.
fbname = |CONVERSION_EXIT_{ wdfies-convexit }_INPUT|.
CALL FUNCTION fbname
EXPORTING
input = <fh>
IMPORTING
output = <fh>.
CATCH cx_root.
ENDTRY.
ENDCASE.
IF <flow> BETWEEN <fl> AND <fh>.
DELETE ct_range.
CONTINUE.
ENDIF.
TRY.
<fh> = <fh> - 1.
CATCH cx_root.
ENDTRY.
CASE wdfies-convexit.
WHEN ' '.
WHEN 'ALPHA'. <fh> = |{ <fh> ALPHA = IN }|.
WHEN OTHERS.
SHIFT <fh> LEFT DELETING LEADING ' 0'.
TRY.
fbname = |CONVERSION_EXIT_{ wdfies-convexit }_INPUT|.
CALL FUNCTION fbname
EXPORTING
input = <fh>
IMPORTING
output = <fh>.
CATCH cx_root.
ENDTRY.
ENDCASE.
IF <fl> = <fh>.
<fo> = 'EQ'.
CLEAR <fh>.
ENDIF.
APPEND <fsrline> TO <fsrtable>.
<fl> = <flow>.
<fh> = <flow>.
<fo> = 'BT'.
DELETE ct_range.
ENDLOOP.
IF <fl> = <fh>.
<fo> = 'EQ'.
CLEAR <fh>.
ENDIF.
APPEND <fsrline> TO <fsrtable>.
APPEND LINES OF <fsrtable> TO ct_range.
CLEAR <fsrtable>. FREE <fsrtable>.
CLEAR <fsrline>. FREE <fsrline>.
ENDMETHOD.
Code: Alles auswählen.
REPORT zknobel_condense_rangeb.
CLASS lcl_knobel DEFINITION FINAL.
PUBLIC SECTION.
CLASS-METHODS: condense_range CHANGING ct_range TYPE STANDARD TABLE.
PRIVATE SECTION.
CLASS-METHODS:
can_be_condensed IMPORTING it_range TYPE STANDARD TABLE
RETURNING VALUE(rv_can_be_condensed) TYPE abap_bool,
partition IMPORTING it_range TYPE STANDARD TABLE
iv_typekind TYPE abap_typekind
CHANGING ct_range_sign_i TYPE STANDARD TABLE
ct_range_sign_e TYPE STANDARD TABLE
ct_range_wtf TYPE STANDARD TABLE,
get_successor IMPORTING iv_typekind TYPE abap_typekind
CHANGING cv_value TYPE any,
get_predecessor IMPORTING iv_typekind TYPE abap_typekind
CHANGING cv_value TYPE any,
condense_bt_range IMPORTING iv_typekind TYPE abap_typekind
CHANGING ct_bt_range TYPE STANDARD TABLE,
merge_ibt_ebt IMPORTING iv_typekind TYPE abap_typekind
it_range_wtf TYPE STANDARD TABLE
CHANGING ct_bt_range_i TYPE STANDARD TABLE
ct_bt_range_e TYPE STANDARD TABLE.
ENDCLASS.
END-OF-SELECTION.
*TABLES: vbak.
*SELECT-OPTIONS: s_vbeln FOR vbak-vbeln.
* DATA(gt_range) = s_vbeln[].
* lcl_knobel=>condense_range( CHANGING ct_range = gt_range ).
*
* SELECT * FROM vbak WHERE vbeln IN @s_vbeln ORDER BY vbeln INTO TABLE @DATA(lt_data1).
* SELECT * FROM vbak WHERE vbeln IN @gt_range ORDER BY vbeln INTO TABLE @DATA(lt_data2).
* ASSERT lt_data1 = lt_data2.
SELECT 'I' AS sign,
'EQ' AS option,
cdat AS low,
cdat AS high
FROM reposrc
WHERE cnam = 'SAP'
INTO TABLE @DATA(gt_range).
lcl_knobel=>condense_range( CHANGING ct_range = gt_range ).
SORT gt_range BY low.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(go_salv) " Basisklasse einfache ALV Tabellen
CHANGING t_table = gt_range ).
go_salv->get_display_settings( )->set_list_header( CONV #( |{ 'Einträge in Range:'(001) } { lines( gt_range ) }| ) ).
go_salv->get_functions( )->set_all( ).
go_salv->display( ).
CATCH cx_salv_msg INTO DATA(lo_cx).
MESSAGE lo_cx TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
CLASS lcl_knobel IMPLEMENTATION.
METHOD condense_range.
DATA:lr_data TYPE REF TO data,
lv_typekind TYPE abap_typekind.
FIELD-SYMBOLS: <lt_r_sign_i> TYPE STANDARD TABLE,
<lt_r_sign_e> TYPE STANDARD TABLE,
<lt_r_wtf> TYPE STANDARD TABLE,
<ls_range> TYPE any,
<lv_sign> TYPE char1,
<lv_option> TYPE char2,
<lv_low> TYPE any,
<lv_high> TYPE any.
*--------------------------------------------------------------------*
* 1. Muss überhaupt was getan werden?
*--------------------------------------------------------------------*
IF can_be_condensed( ct_range ) = abap_false.
RETURN.
ENDIF.
*--------------------------------------------------------------------*
* 2. Ein paar Vorbereitungen und dann normieren
* Normieren = alles was möglich ist nach BT umwandeln
*--------------------------------------------------------------------*
CREATE DATA lr_data LIKE LINE OF ct_range.
ASSIGN lr_data->* TO <ls_range>.
ASSIGN COMPONENT 'SIGN ' OF STRUCTURE <ls_range> TO <lv_sign> .
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <ls_range> TO <lv_option> .
ASSIGN COMPONENT 'LOW ' OF STRUCTURE <ls_range> TO <lv_low> .
ASSIGN COMPONENT 'HIGH ' OF STRUCTURE <ls_range> TO <lv_high> .
lv_typekind = cl_abap_typedescr=>describe_by_data( <lv_low> )->type_kind.
CREATE DATA lr_data LIKE ct_range. ASSIGN lr_data->* TO <lt_r_sign_i>.
CREATE DATA lr_data LIKE ct_range. ASSIGN lr_data->* TO <lt_r_sign_e>.
CREATE DATA lr_data LIKE ct_range. ASSIGN lr_data->* TO <lt_r_wtf>.
partition( EXPORTING it_range = ct_range
iv_typekind = lv_typekind
CHANGING ct_range_sign_i = <lt_r_sign_i>
ct_range_sign_e = <lt_r_sign_e>
ct_range_wtf = <lt_r_wtf>
).
*--------------------------------------------------------------------*
* 3. Die einzelnen Teile intern optimieren und dann zusammenfügen
*--------------------------------------------------------------------*
CLEAR ct_range.
APPEND LINES OF <lt_r_wtf> TO ct_range. " All das was nicht optimiert werden konnte
* I BT verdichten
condense_bt_range( EXPORTING iv_typekind = lv_typekind
CHANGING ct_bt_range = <lt_r_sign_i> ).
* E BT verdichten
condense_bt_range( EXPORTING iv_typekind = lv_typekind
CHANGING ct_bt_range = <lt_r_sign_e> ).
* I BT und E BT abmischen
merge_ibt_ebt( EXPORTING iv_typekind = lv_typekind
it_range_wtf = <lt_r_wtf>
CHANGING ct_bt_range_i = <lt_r_sign_i>
ct_bt_range_e = <lt_r_sign_e> ).
APPEND LINES OF <lt_r_sign_i> TO ct_range. " All das was nicht optimiert werden konnte
APPEND LINES OF <lt_r_sign_e> TO ct_range. " All das was nicht optimiert werden konnte
*--------------------------------------------------------------------*
* Am Ende "Hübsch" machen BT mit HIGH = LOW --> EQ
*--------------------------------------------------------------------*
LOOP AT ct_range ASSIGNING FIELD-SYMBOL(<ls_range_beautify>).
<ls_range> = <ls_range_beautify>.
IF <lv_option> = 'BT'
AND <lv_low> = <lv_high>.
<lv_option> = 'EQ'.
CLEAR <lv_high>.
<ls_range_beautify> = <ls_range>.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD condense_bt_range.
DATA: lr_data TYPE REF TO data.
FIELD-SYMBOLS: <lv_low> TYPE any,
<lv_high> TYPE any,
<lv_last_low> TYPE any,
<lv_last_high> TYPE any,
<lv_value> TYPE any.
*--------------------------------------------------------------------*
* Duplikate entfernen und in sortierte Reihenfolge bringen
*--------------------------------------------------------------------*
SORT ct_bt_range BY ('LOW') ('HIGH').
DELETE ADJACENT DUPLICATES FROM ct_bt_range COMPARING ALL FIELDS.
LOOP AT ct_bt_range ASSIGNING FIELD-SYMBOL(<ls_row>).
AT FIRST.
ASSIGN <ls_row> TO FIELD-SYMBOL(<ls_last_row>).
CONTINUE.
ENDAT.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_row> TO <lv_low>.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_last_row> TO <lv_last_low>.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_row> TO <lv_high>.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_last_row> TO <lv_last_high>.
IF <lv_value> IS NOT ASSIGNED.
CREATE DATA lr_data LIKE <lv_low>.
ASSIGN lr_data->* TO <lv_value>.
ENDIF.
*--------------------------------------------------------------------*
* Low diese Zeile <= high letze Zeile --> Letzte Zeile bekommen den höheren HIGH-Wert der beiden, aktuelle Zeile wird gelöscht
*--------------------------------------------------------------------*
IF <lv_low> <= <lv_last_high>.
IF <lv_high> > <lv_last_high>.
<lv_last_high> = <lv_high>.
ENDIF.
DELETE ct_bt_range. " SLIN Warnung - aber ich weiß ja was ich tue *hüstel*
CONTINUE.
ENDIF.
*--------------------------------------------------------------------*
* Low diese Zeile = Nachfolger high letze Zeile + --> Zusammenkleben
*--------------------------------------------------------------------*
<lv_value> = <lv_last_high>.
get_successor( EXPORTING iv_typekind = iv_typekind
CHANGING cv_value = <lv_value> ).
IF <lv_low> = <lv_value>.
<lv_last_high> = <lv_high>.
DELETE ct_bt_range. " SLIN Warnung - aber ich weiß ja was ich tue *hüstel*
CONTINUE.
ENDIF.
ASSIGN <ls_row> TO <ls_last_row>.
ENDLOOP.
ENDMETHOD.
METHOD merge_ibt_ebt.
*--------------------------------------------------------------------*
* Es gibt 5 Fälle für Fälle in E BT
* 1 - E BT disjunkt zu allen I BT Intervall --> E BT kann entsorgt werden falls keine Patterns vorhanden sind
* 2 - E BT überlappt I BT Intervall --> I BT kann entsorgt werden
* 3 - E BT schneidet I BT Intervall von unten --> Reduzierung der SelOpts möglich falls keine Patterns vorhanden sind I BT reduzieren, E BT entsorgen
* 4 - E BT schneidet I BT Intervall von oben --> Reduzierung der SelOpts möglich falls keine Patterns vorhanden sind I BT reduzieren, E BT entsorgen
* 5 - E BT innerhalb I BT Intervall --> keine wesentliche Reduzierung der SelOpts möglich
*--------------------------------------------------------------------*
*--------------------------------------------------------------------*
* Der geschachtelte Loop könnte evtl. optimiert werden, da beide Tabellen sortiert sind,
* Aber wenn beide nicht arg groß sind, ist es besser zu lesen
*--------------------------------------------------------------------*
LOOP AT ct_bt_range_e ASSIGNING FIELD-SYMBOL(<ls_range_e>).
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_range_e> TO FIELD-SYMBOL(<lv_low_e>).
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_range_e> TO FIELD-SYMBOL(<lv_high_e>).
LOOP AT ct_bt_range_i ASSIGNING FIELD-SYMBOL(<ls_range_i>). "#EC CI_NESTED
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_range_i> TO FIELD-SYMBOL(<lv_low_i>).
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_range_i> TO FIELD-SYMBOL(<lv_high_i>).
* Fall 2
IF <lv_low_e> <= <lv_low_i>
AND <lv_high_e> >= <lv_high_i>.
DELETE ct_bt_range_i.
CONTINUE.
ENDIF.
* Fall 3 und 4: Wenn Keine Patterns vorhanden sind, kann die I BT angepasst werden und E BT entsorgt werden, da die vollst. Range aus Intervallen besteht
IF it_range_wtf IS INITIAL.
* Fall 3 - Schnitt von unten
IF <lv_low_e> <= <lv_low_i>
AND <lv_high_e> >= <lv_low_i>.
<lv_low_i> = <lv_high_e>.
get_successor( EXPORTING iv_typekind = iv_typekind
CHANGING cv_value = <lv_low_i> ).
DELETE ct_bt_range_e.
EXIT.
ENDIF.
* Fall 4 - Schnitt von oben
IF <lv_low_e> <= <lv_high_i>
AND <lv_high_e> >= <lv_high_i>.
<lv_high_i> = <lv_low_e>.
get_predecessor( EXPORTING iv_typekind = iv_typekind
CHANGING cv_value = <lv_high_i> ).
DELETE ct_bt_range_e.
EXIT.
ENDIF.
ENDIF.
ENDLOOP.
ENDLOOP.
*--------------------------------------------------------------------*
* Jetzt kann der 1. Fall angegangen werden
* Nach den obigen Aktionen sind die I BT und E BT Intervalle überlappungsfrei oder E BT liegt vollständig innerhalb von I BT.
* Falls keine
*--------------------------------------------------------------------*
DATA: lv_e_bt_in_i_bt TYPE abap_bool.
IF it_range_wtf IS INITIAL.
LOOP AT ct_bt_range_e ASSIGNING <ls_range_e>.
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_range_e> TO <lv_low_e>.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_range_e> TO <lv_high_e>.
lv_e_bt_in_i_bt = abap_false.
LOOP AT ct_bt_range_i ASSIGNING <ls_range_i>. "#EC CI_NESTED
ASSIGN COMPONENT 'LOW' OF STRUCTURE <ls_range_i> TO <lv_low_i>.
ASSIGN COMPONENT 'HIGH' OF STRUCTURE <ls_range_i> TO <lv_high_i>.
IF <lv_low_e> >= <lv_low_i>
AND <lv_high_e> <= <lv_high_i>.
lv_e_bt_in_i_bt = abap_true.
EXIT.
ENDIF.
ENDLOOP.
IF lv_e_bt_in_i_bt = abap_false.
DELETE ct_bt_range_e.
ENDIF.
ENDLOOP.
ENDIF.
ENDMETHOD.
METHOD partition.
DATA:lr_data TYPE REF TO data.
FIELD-SYMBOLS: <ls_range> TYPE any,
<lv_sign> TYPE char1,
<lv_option> TYPE char2,
<lv_low> TYPE any,
<lv_high> TYPE any,
<lv_min> TYPE any,
<lv_max> TYPE any.
LOOP AT it_range ASSIGNING FIELD-SYMBOL(<ls_range_split>).
AT FIRST.
CREATE DATA lr_data LIKE <ls_range_split>.
ASSIGN lr_data->* TO <ls_range>.
ASSIGN COMPONENT 'SIGN ' OF STRUCTURE <ls_range> TO <lv_sign> .
ASSIGN COMPONENT 'OPTION' OF STRUCTURE <ls_range> TO <lv_option> .
ASSIGN COMPONENT 'LOW ' OF STRUCTURE <ls_range> TO <lv_low> .
ASSIGN COMPONENT 'HIGH ' OF STRUCTURE <ls_range> TO <lv_high> .
lr_data = cl_abap_exceptional_values=>get_min_value( in = <lv_low> ).
ASSIGN lr_data->* TO <lv_min>.
lr_data = cl_abap_exceptional_values=>get_max_value( in = <lv_low> ).
ASSIGN lr_data->* TO <lv_max>.
IF iv_typekind = 'C'.
CLEAR <lv_min> WITH '0'.
CLEAR <lv_max> WITH '9'.
ENDIF.
ENDAT.
<ls_range> = <ls_range_split>.
CASE <lv_option>.
WHEN 'EQ'." EQ -> BT mit High = low
<lv_option> = 'BT'.
<lv_high> = <lv_low>.
WHEN 'GE'. " GE --> BT mit high = max value
<lv_option> = 'BT'.
<lv_high> = <lv_max>.
WHEN 'GT'. " Wie GE, aber low = Nachfolger
<lv_option> = 'BT'.
<lv_high> = <lv_max>.
get_successor( EXPORTING iv_typekind = iv_typekind
CHANGING cv_value = <lv_low> ).
WHEN 'LE'. " LE --> BT mit low = min
<lv_option> = 'BT'.
<lv_high> = <lv_low>.
<lv_low> = <lv_min>.
WHEN 'LT'. " Wie LE, aber High = Vorgänger
<lv_option> = 'BT'.
<lv_high> = <lv_low>.
get_predecessor( EXPORTING iv_typekind = iv_typekind
CHANGING cv_value = <lv_high> ).
<lv_low> = <lv_min>.
WHEN 'BT'. " aleady ok
* WHEN 'NB'. " Alles außerhalb eines Intervalls --> !! I NB ungleich E BT wenn man die komplette Range betrachtet
* WHEN 'NE'. " Alles außer einem Wert --> !! I NE ungleich E EQ wenn man die komplette Range betrachtet
* WHEN 'CP'. " Patterns are too complex -> leave as is
* WHEN 'NP'. " Patterns are too complex -> leave as is
WHEN OTHERS.
APPEND <ls_range> TO ct_range_wtf.
CONTINUE.
ENDCASE.
CASE <lv_sign>.
WHEN 'I'.
APPEND <ls_range> TO ct_range_sign_i.
WHEN 'E'.
APPEND <ls_range> TO ct_range_sign_e.
WHEN OTHERS.
ct_range_wtf = it_range.
CLEAR: ct_range_sign_i,
ct_range_sign_e.
RETURN. " Das darf nicht sein --> Tabelle ungeändert zurückgeben
ENDCASE.
ENDLOOP.
ENDMETHOD.
METHOD get_successor.
CASE iv_typekind.
WHEN 'C'. " Numeric field --> use ALPHA-conversion
IF cv_value CO '0123456789'.
cv_value = cv_value + 1.
cv_value = |{ cv_value ALPHA = IN }|.
ENDIF.
WHEN OTHERS.
cv_value = cv_value + 1.
ENDCASE.
ENDMETHOD.
METHOD get_predecessor.
CASE iv_typekind.
WHEN 'C'. " Numeric field --> use ALPHA-conversion
IF cv_value CO '0123456789'.
cv_value = cv_value - 1.
cv_value = |{ cv_value ALPHA = IN }|.
ENDIF.
WHEN OTHERS.
cv_value = cv_value - 1.
ENDCASE.
ENDMETHOD.
METHOD can_be_condensed.
rv_can_be_condensed = abap_false.
*--------------------------------------------------------------------*
* Leere Tabelle ungeändert zurückgeben , Range mit 1 Zeile kann im allgemeinen nicht optimiert werden
*--------------------------------------------------------------------*
IF lines( it_range ) <= 1.
RETURN.
ENDIF.
*--------------------------------------------------------------------*
* Ist es eine Range-Tabelle?
*--------------------------------------------------------------------*
LOOP AT CAST cl_abap_structdescr( cl_abap_structdescr=>describe_by_data( it_range[ 1 ] ) )->components ASSIGNING FIELD-SYMBOL(<ls_component>) .
CASE sy-tabix.
WHEN 1.
IF <ls_component>-name <> 'SIGN'. RETURN. ENDIF.
WHEN 2.
IF <ls_component>-name <> 'OPTION'. RETURN. ENDIF.
WHEN 3.
IF <ls_component>-name <> 'LOW'. RETURN. ENDIF.
WHEN 4.
IF <ls_component>-name <> 'HIGH'. RETURN. ENDIF.
WHEN OTHERS.
RETURN.
ENDCASE.
ENDLOOP.
*--------------------------------------------------------------------*
* Ok - alle Test sind zufriedenstellend
*--------------------------------------------------------------------*
rv_can_be_condensed = abap_true.
ENDMETHOD.
ENDCLASS.
Folgende Benutzer bedankten sich beim Autor black_adept für den Beitrag (Insgesamt 3):
Murdock • Thomas R. • ewx
Code: Alles auswählen.
CLASS zcl_range DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
CLASS-METHODS condense_range_option
IMPORTING
iv_option TYPE tvarv_opti
CHANGING
ct_range TYPE ANY TABLE .
"! <p class="shorttext synchronized" lang="de">Verdichten einer Select-Option Range</p>
CLASS-METHODS condense_range
CHANGING
ct_range TYPE ANY TABLE .
CLASS-METHODS init
IMPORTING
it_datatab TYPE any .
PROTECTED SECTION.
PRIVATE SECTION.
TYPES: BEGIN OF mty_datatype,
typekind TYPE abap_typekind,
length TYPE i,
decimals TYPE i,
END OF mty_datatype.
CLASS-METHODS: get_next_value
IMPORTING
iv_wert TYPE any
iv_datatype TYPE abap_typekind
EXPORTING
VALUE(ev_naechster_wert) TYPE any,
is_in_range
IMPORTING
it_range TYPE rseloption
iv_value TYPE rsdsselopt-low
RETURNING
VALUE(rv_result) TYPE abap_bool,
condense_range_eq
CHANGING
ct_range TYPE ANY TABLE,
condense_range_bt
CHANGING
ct_range TYPE ANY TABLE,
condense_range_eq_in_bt
CHANGING
ct_range TYPE ANY TABLE,
get_datatype_col
IMPORTING
iv_spalte TYPE name_feld
it_datatab TYPE ANY TABLE
RETURNING
VALUE(rs_dtype) TYPE mty_datatype,
create_ref_to_data
IMPORTING
is_datatype TYPE mty_datatype
RETURNING
VALUE(rr_dref) TYPE REF TO data,
is_countable_type
IMPORTING
iv_typekind TYPE abap_typekind
RETURNING
VALUE(rv_result) TYPE abap_bool,
set_datatype_low
IMPORTING
is_datatype TYPE mty_datatype,
get_datatype_low
RETURNING
VALUE(rs_datatype) TYPE mty_datatype.
CONSTANTS: mc_loeschen(1) VALUE '#'.
CLASS-DATA ms_datatype_low TYPE mty_datatype.
ENDCLASS.
CLASS zcl_range IMPLEMENTATION.
METHOD condense_range.
DATA(ls_datatype) = get_datatype_col( iv_spalte = 'LOW' it_datatab = ct_range ).
set_datatype_low( is_datatype = ls_datatype ).
" Den Kram nur machen, wenn LOW einen Datentyp hat, den man evtl. mit + 1 hochzählen kann
IF is_countable_type( ls_datatype-typekind ).
condense_range_option( EXPORTING iv_option = 'EQ' CHANGING ct_range = ct_range ).
condense_range_option( EXPORTING iv_option = 'BT' CHANGING ct_range = ct_range ).
" Schauen, ob eine EQ Zeile mittlerweile in einen BT Bereich fällt
condense_range_option( EXPORTING iv_option = 'ET' CHANGING ct_range = ct_range ).
ENDIF.
ENDMETHOD.
METHOD condense_range_bt.
DATA: lt_range TYPE rseloption.
lt_range = CORRESPONDING #( ct_range ).
SORT lt_range BY sign option high low.
DELETE ADJACENT DUPLICATES FROM lt_range.
READ TABLE lt_range WITH KEY sign = 'I' option = 'BT' ASSIGNING FIELD-SYMBOL(<ls_aktuelle_zeile>).
IF sy-subrc = 0.
DATA(lv_zeilenindex) = sy-tabix.
DATA(lv_letzte_zeile) = lines( lt_range ).
DATA(lv_weitermachen) = abap_true.
DATA(ls_datatype) = get_datatype_low( ).
IF ls_datatype IS INITIAL.
ls_datatype = get_datatype_col( iv_spalte = 'LOW' it_datatab = ct_range ).
set_datatype_low( is_datatype = ls_datatype ).
ENDIF.
" Feldsymbol mit passenden Datentyp (wie -low) zum Hochzählen erstellen
DATA(lr_dref) = create_ref_to_data( ls_datatype ).
ASSIGN lr_dref->* TO FIELD-SYMBOL(<lv_naechster_wert>).
WHILE lv_zeilenindex < lv_letzte_zeile AND lv_weitermachen = abap_true.
lv_zeilenindex = lv_zeilenindex + 1.
ASSIGN lt_range[ lv_zeilenindex ] TO FIELD-SYMBOL(<ls_naechste_zeile>).
get_next_value( EXPORTING iv_wert = <ls_aktuelle_zeile>-high iv_datatype = ls_datatype-typekind
IMPORTING ev_naechster_wert = <lv_naechster_wert> ).
" Passt der aktuelle Höchstwert in den nächsten Bereich?
IF <ls_naechste_zeile>-sign = 'I' AND <ls_naechste_zeile>-option = 'BT'
AND <ls_aktuelle_zeile>-high <= <ls_naechste_zeile>-high
AND ( ( <ls_aktuelle_zeile>-high >= <ls_naechste_zeile>-low )
OR ( <ls_naechste_zeile>-low = <lv_naechster_wert> ) ). " Bereiche genau angrenzend?
" Nächsten Höchstwert als neuen Höchstwert übernehmen
<ls_aktuelle_zeile>-high = <ls_naechste_zeile>-high.
IF <ls_aktuelle_zeile>-low > <ls_naechste_zeile>-low.
<ls_aktuelle_zeile>-low = <ls_naechste_zeile>-low.
ENDIF.
<ls_naechste_zeile>-option = mc_loeschen.
ELSE.
ASSIGN lt_range[ lv_zeilenindex ] TO <ls_aktuelle_zeile>.
ENDIF.
ENDWHILE.
DELETE lt_range WHERE option = mc_loeschen.
ct_range = CORRESPONDING #( lt_range ).
ENDIF.
ENDMETHOD.
METHOD condense_range_eq.
DATA: lt_range TYPE rseloption.
lt_range = CORRESPONDING #( ct_range ).
SORT lt_range BY sign option low.
DELETE ADJACENT DUPLICATES FROM lt_range.
READ TABLE lt_range WITH KEY sign = 'I' option = 'EQ' ASSIGNING FIELD-SYMBOL(<ls_aktuelle_zeile>).
IF sy-subrc = 0.
DATA(lv_zeilenindex) = sy-tabix.
DATA(lv_letzte_zeile) = lines( lt_range ).
DATA(lv_aktuell_max_wert) = <ls_aktuelle_zeile>-low.
DATA(lv_weitermachen) = abap_true.
DATA(ls_datatype) = get_datatype_low( ).
IF ls_datatype IS INITIAL.
ls_datatype = get_datatype_col( iv_spalte = 'LOW' it_datatab = ct_range ).
set_datatype_low( is_datatype = ls_datatype ).
ENDIF.
" Feldsymbol mit passenden Datentyp (wie -low) zum Hochzählen erstellen
DATA(lr_dref) = create_ref_to_data( ls_datatype ).
ASSIGN lr_dref->* TO FIELD-SYMBOL(<lv_naechster_wert>).
WHILE lv_zeilenindex < lv_letzte_zeile AND lv_weitermachen = abap_true.
lv_zeilenindex = lv_zeilenindex + 1.
ASSIGN lt_range[ lv_zeilenindex ] TO FIELD-SYMBOL(<ls_naechste_zeile>).
get_next_value( EXPORTING iv_wert = lv_aktuell_max_wert iv_datatype = ls_datatype-typekind
IMPORTING ev_naechster_wert = <lv_naechster_wert> ).
" Ist der folgende Wert = der aktuelle Wert + 1?
IF <ls_naechste_zeile>-sign = 'I' AND <ls_naechste_zeile>-option = 'EQ'
AND condense( val = |{ <lv_naechster_wert> }| )
= condense( val = |{ <ls_naechste_zeile>-low ALPHA = OUT }| ).
" Werte mittels Option BT zusammenfassen
<ls_aktuelle_zeile>-option = 'BT'.
<ls_aktuelle_zeile>-high = <ls_naechste_zeile>-low. "CONV #( <lv_naechster_wert> ).
<ls_naechste_zeile>-option = mc_loeschen . " für späteres Löschen
ELSE.
ASSIGN lt_range[ lv_zeilenindex ] TO <ls_aktuelle_zeile>.
" Wenn eine neue Option kommt, kann die Schleife verlassen werden
IF <ls_naechste_zeile>-option <> 'EQ'.
lv_weitermachen = abap_false.
ENDIF.
ENDIF.
lv_aktuell_max_wert = <ls_naechste_zeile>-low.
ENDWHILE.
DELETE lt_range WHERE option = mc_loeschen.
ct_range = CORRESPONDING #( lt_range ).
ENDIF.
ENDMETHOD.
METHOD condense_range_eq_in_bt.
" Löschen der EQ Werte, die in einem BT Bereich sind
DATA: lt_range TYPE rseloption.
lt_range = CORRESPONDING #( ct_range ).
SORT lt_range BY sign option low.
DELETE ADJACENT DUPLICATES FROM lt_range.
READ TABLE lt_range WITH KEY sign = 'I' option = 'EQ' ASSIGNING FIELD-SYMBOL(<ls_aktuelle_zeile>).
IF sy-subrc = 0.
DATA(lv_zeilenindex) = sy-tabix.
DATA(lv_letzte_zeile) = lines( lt_range ).
DATA(lv_weitermachen) = abap_true.
WHILE lv_zeilenindex <= lv_letzte_zeile AND lv_weitermachen = abap_true.
IF is_in_range( it_range = lt_range iv_value = <ls_aktuelle_zeile>-low ).
<ls_aktuelle_zeile>-option = mc_loeschen.
ENDIF.
lv_zeilenindex = lv_zeilenindex + 1.
ASSIGN lt_range[ lv_zeilenindex ] TO <ls_aktuelle_zeile>.
" Wenn eine neue Option kommt, kann die Schleife verlassen werden
IF <ls_aktuelle_zeile>-option <> 'EQ'.
lv_weitermachen = abap_false.
ENDIF.
ENDWHILE.
DELETE lt_range WHERE option = mc_loeschen.
ct_range = CORRESPONDING #( lt_range ).
ENDIF.
ENDMETHOD.
METHOD condense_range_option.
CASE iv_option.
WHEN 'EQ'.
condense_range_eq( CHANGING ct_range = ct_range ).
WHEN 'BT'.
condense_range_bt( CHANGING ct_range = ct_range ).
WHEN 'ET'.
condense_range_eq_in_bt( CHANGING ct_range = ct_range ).
ENDCASE.
ENDMETHOD.
METHOD create_ref_to_data.
IF is_datatype-typekind = cl_abap_typedescr=>typekind_date.
CREATE DATA rr_dref TYPE (is_datatype-typekind).
ELSE.
CREATE DATA rr_dref TYPE (is_datatype-typekind) LENGTH is_datatype-length.
ENDIF.
ENDMETHOD.
METHOD get_datatype_col.
DATA: lr_datatab TYPE REF TO data.
FIELD-SYMBOLS: <lt_datatab> TYPE table.
GET REFERENCE OF it_datatab INTO lr_datatab.
ASSIGN lr_datatab->* TO <lt_datatab>.
ASSIGN COMPONENT iv_spalte OF STRUCTURE <lt_datatab>[ 1 ] TO FIELD-SYMBOL(<ls_field>).
IF <ls_field> IS ASSIGNED.
DATA(lr_type_descr) = cl_abap_typedescr=>describe_by_data( <ls_field> ).
rs_dtype = VALUE #( typekind = lr_type_descr->type_kind
length = CAST cl_abap_elemdescr( lr_type_descr )->output_length
decimals = lr_type_descr->decimals ) .
ENDIF.
ENDMETHOD.
METHOD get_datatype_low.
rs_datatype = ms_datatype_low.
ENDMETHOD.
METHOD get_next_value.
TRY.
CASE iv_datatype.
WHEN cl_abap_typedescr=>typekind_date.
ev_naechster_wert = CONV dats( iv_wert ) + 1.
WHEN OTHERS.
ev_naechster_wert = iv_wert + 1 .
ENDCASE.
CATCH CX_SY_CONVERSION_NO_NUMBER.
ev_naechster_wert = iv_wert.
ENDTRY.
ENDMETHOD.
METHOD init.
" Für evtl. direkten Aufruf der condense_range_option Methode (nicht über condense_range() ), z.B. Unittest
DATA(ls_datatype) = get_datatype_col( iv_spalte = 'LOW' it_datatab = it_datatab ).
set_datatype_low( is_datatype = ls_datatype ).
ENDMETHOD.
METHOD is_countable_type.
IF iv_typekind = cl_abap_typedescr=>typekind_date OR
iv_typekind = cl_abap_typedescr=>typekind_char OR
iv_typekind = cl_abap_typedescr=>typekind_clike OR
iv_typekind = cl_abap_typedescr=>typekind_csequence OR
iv_typekind = cl_abap_typedescr=>typekind_int OR
iv_typekind = cl_abap_typedescr=>typekind_int1 OR
iv_typekind = cl_abap_typedescr=>typekind_int8 OR
iv_typekind = cl_abap_typedescr=>typekind_int2 OR
iv_typekind = cl_abap_typedescr=>typekind_num OR
iv_typekind = cl_abap_typedescr=>typekind_numeric OR
iv_typekind = cl_abap_typedescr=>typekind_string.
rv_result = abap_true.
ENDIF.
ENDMETHOD.
METHOD is_in_range.
LOOP AT it_range ASSIGNING FIELD-SYMBOL(<ls_range>) WHERE low <= iv_value AND high >= iv_value
AND sign = 'I' AND option = 'BT'.
rv_result = abap_true.
RETURN.
ENDLOOP.
ENDMETHOD.
METHOD set_datatype_low.
ms_datatype_low = is_datatype.
ENDMETHOD.
ENDCLASS.