Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Maximum data in a range

deca
Explorer

Hello,

I have a range of data and I want to know what is the maximum data included in.

I apologize for my English, I try to explain better using some samples

DATA: lt_datum TYPE RANGE OF datum.

_______

SAMPLE 1

SIGN  OPTION  LOW          HIGH
  I     GT    20.02.2020

In this case it's easy: maximum value is 31.12.9999

_______

SAMPLE 2

SIGN  OPTION  LOW          HIGH
  I     BT    20.02.2020   31.12.2020
  E     EQ    30.09.2020
  I     BT    01.01.2019   31.12.2019
  I     LE    01.01.2016

I know this is quite a stupid mode to fill in a range, but I want to know maximum value, 31.12.2020 in such case.

_______

SAMPLE 3

SIGN  OPTION  LOW          HIGH
  E     BT    20.02.2020   31.12.2020
  E     EQ    30.09.2020
  I     BT    01.01.2019   31.12.2019
  I     LE    01.01.2016

Maybe tis is even a more stupid way to fill in a range... but I want to know maximum value, 31.12.2019 in such case.

So... is there an easy way to get this date? I could easily do if I would have a table with all data available, as I would do a SELECT MAX using this range... bua I have never seen such a table.

So, have you got any suggestion?

Thank you in advance.

Regards,

Guido

1 ACCEPTED SOLUTION

FredericGirod
Active Contributor

My proposal (with Abap Unit)

I choose another way than yormanoviedo, I prefer to simplified the list before proceed it.

The class for the calculation

CLASS lc_date_calculation DEFINITION.

  PUBLIC SECTION.
    METHODS get_max_date_from_range
      IMPORTING
        dates_range     TYPE trgr_date
      RETURNING
        VALUE(max_date) TYPE sydatum.

    METHODS convert_range_single_values
      IMPORTING
        dates_range                   TYPE trgr_date
      RETURNING
        VALUE(dates_range_simplified) TYPE trgr_date.
ENDCLASS.


CLASS lc_date_calculation IMPLEMENTATION.

  METHOD get_max_date_from_range.

    DATA(simplified_range) = convert_range_single_values( dates_range ).
    " if there is for the same date I EQ and E EQ   the E is dominant
    SORT simplified_range BY low descending sign ascending.
    DELETE ADJACENT DUPLICATES FROM simplified_range COMPARING low sign.

    " Find if there is GT.
    READ TABLE simplified_range
         INTO DATA(range_line_gt)
         WITH KEY sign   = 'I'
                  option = 'GT'.
    IF sy-subrc EQ 0.
      " Find if there is a excluding GT.
      READ TABLE simplified_range
         INTO DATA(range_line_gt_excluding)
         WITH KEY sign   = 'E'
                  option = 'GT'.
      IF sy-subrc EQ 0.
        IF range_line_gt_excluding-low GT range_line_gt-low.
          max_date = range_line_gt_excluding-low - 1.
          RETURN.
        ENDIF.
      ELSE.
        max_date = '99991231'.
        RETURN.
      ENDIF.
    ENDIF.

    " Otherwise get the max I EQ value.
    READ TABLE simplified_range
         INTO DATA(range_line)
         WITH KEY sign = 'I'
                  option = 'EQ'.
    IF sy-subrc EQ 0.
      max_date = range_line-low.
    ENDIF.

  ENDMETHOD.


  METHOD convert_range_single_values.

    LOOP AT dates_range
         REFERENCE INTO DATA(lo_line_range).
      IF lo_line_range->option EQ 'BT'.
        DATA(calculated_date) = lo_line_range->low.
        DO.
          APPEND VALUE trgs_datum( sign   = lo_line_range->sign
                                   option = 'EQ'
                                   low    = calculated_date      ) TO dates_range_simplified.
          calculated_date = calculated_date + 1.
          IF calculated_date GT lo_line_range->high OR
             lo_line_range->high IS INITIAL.
            EXIT.
          ENDIF.
        ENDDO.
      ELSE.
        APPEND VALUE trgs_datum( sign   = lo_line_range->sign
                                 option = lo_line_range->option
                                 low    = lo_line_range->low      ) TO dates_range_simplified.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

and the corresponding ABAP Unit, it is easy to test your example. ( you just have to do a CTRL + SHIFT + F10 to run it).

CLASS ltc_date_calculation DEFINITION
      FOR TESTING
      RISK LEVEL HARMLESS
      DURATION SHORT
      FINAL.

  PRIVATE SECTION.
    METHODS setup.
    METHODS check_convert_range_is_ok FOR TESTING.
    METHODS check_max_date_exampl_1   FOR TESTING.
    METHODS check_max_date_exampl_2   FOR TESTING.
    METHODS check_max_date_exampl_3   FOR TESTING.

    DATA o_cut TYPE REF TO lc_date_calculation.

ENDCLASS.


CLASS ltc_date_calculation IMPLEMENTATION.

  METHOD setup.
    o_cut = NEW #( ).
  ENDMETHOD.


  METHOD check_convert_range_is_ok.
    " Given I have a strange example
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'BT'  low = '20200101' high = '20200103' )
                                            ( sign = 'I'  option = 'NE'  low = '20200105' ) ).
    " When I call the transform method
    DATA(my_result_range) = o_cut->convert_range_single_values( my_input_range ).
    " Then I should have 4 entries in the result table with good entries
    cl_abap_unit_assert=>assert_equals(
      act = lines( my_result_range )
      exp = 4
      msg = 'You should have 4 lines in the result !' ).
    cl_abap_unit_assert=>assert_table_contains(
      table = my_result_range
      line  = VALUE trgs_datum( sign = 'I' option = 'EQ' low = '20200102'  ) ).
    cl_abap_unit_assert=>assert_table_contains(
      table = my_result_range
      line  = VALUE trgs_datum( sign = 'I' option = 'NE' low = '20200105'  ) ).
  ENDMETHOD.


  METHOD check_max_date_exampl_1.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'GT'  low = '20200220'  ) ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '99991231' ).
  ENDMETHOD.


  METHOD check_max_date_exampl_2.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'BT'  low = '20200220' high = '20201231' )
                                            ( sign = 'E'  option = 'EQ'  low = '20200930'                   )
                                            ( sign = 'I'  option = 'BT'  low = '20190101' high = '20191231' )
                                            ( sign = 'I'  option = 'LE'  low = '20160101'                   )  ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '20201231' ).
  ENDMETHOD.



METHOD check_max_date_exampl_3.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'E'  option = 'BT'  low = '20200220' high = '20201231' )
                                            ( sign = 'E'  option = 'EQ'  low = '20200930'                   )
                                            ( sign = 'I'  option = 'BT'  low = '20190101' high = '20191231' )
                                            ( sign = 'I'  option = 'LE'  low = '20160101'                   )  ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '20191231' ).
  ENDMETHOD.


ENDCLASS.

I think the code is not complete, there were examples not working.

I manage I GT Date_1 & E GT Date_2 but you could have other strange case just E GT Date ...

11 REPLIES 11

0 Kudos

Hola, hice este pequeño programa que bien puede poner en un MF. Lo probé con las muestras que dejaste.

Espero te sirva....

*&---------------------------------------------------------------------*
*& Report Y_RANGE_FECHA
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
REPORT y_range_fecha.

SELECTION-SCREEN: BEGIN OF BLOCK bl01 .
SELECT-OPTIONS: lt_datum FOR sy-datum.
SELECTION-SCREEN: END OF BLOCK bl01.

START-OF-SELECTION.

  SORT lt_datum BY high DESCENDING.

  DATA(lv_fecha_alta) = '00000000'.

  LOOP AT lt_datum INTO DATA(wa_rango).
    IF wa_rango-sign EQ 'E'.
      CONTINUE.
    ELSE.
      IF lv_fecha_alta EQ '00000000'.
        lv_fecha_alta = wa_rango-high.
      ELSE.
        IF wa_rango-low GT lv_fecha_alta.
          lv_fecha_alta = wa_rango-low.
        ENDIF.
        IF wa_rango-high GT lv_fecha_alta.
          lv_fecha_alta = wa_rango-high.
        ENDIF.
      ENDIF.

      CASE wa_rango-option.
        WHEN 'GT' OR 'GE'.
          IF wa_rango-high EQ '00000000'.
            lv_fecha_alta = '31.12.9999'.
          ENDIF.
      ENDCASE.
    ENDIF.
  ENDLOOP.

  WRITE: 'La fecha más alta del rango es: '.
  WRITE: lv_fecha_alta.

FredericGirod
Active Contributor

My proposal (with Abap Unit)

I choose another way than yormanoviedo, I prefer to simplified the list before proceed it.

The class for the calculation

CLASS lc_date_calculation DEFINITION.

  PUBLIC SECTION.
    METHODS get_max_date_from_range
      IMPORTING
        dates_range     TYPE trgr_date
      RETURNING
        VALUE(max_date) TYPE sydatum.

    METHODS convert_range_single_values
      IMPORTING
        dates_range                   TYPE trgr_date
      RETURNING
        VALUE(dates_range_simplified) TYPE trgr_date.
ENDCLASS.


CLASS lc_date_calculation IMPLEMENTATION.

  METHOD get_max_date_from_range.

    DATA(simplified_range) = convert_range_single_values( dates_range ).
    " if there is for the same date I EQ and E EQ   the E is dominant
    SORT simplified_range BY low descending sign ascending.
    DELETE ADJACENT DUPLICATES FROM simplified_range COMPARING low sign.

    " Find if there is GT.
    READ TABLE simplified_range
         INTO DATA(range_line_gt)
         WITH KEY sign   = 'I'
                  option = 'GT'.
    IF sy-subrc EQ 0.
      " Find if there is a excluding GT.
      READ TABLE simplified_range
         INTO DATA(range_line_gt_excluding)
         WITH KEY sign   = 'E'
                  option = 'GT'.
      IF sy-subrc EQ 0.
        IF range_line_gt_excluding-low GT range_line_gt-low.
          max_date = range_line_gt_excluding-low - 1.
          RETURN.
        ENDIF.
      ELSE.
        max_date = '99991231'.
        RETURN.
      ENDIF.
    ENDIF.

    " Otherwise get the max I EQ value.
    READ TABLE simplified_range
         INTO DATA(range_line)
         WITH KEY sign = 'I'
                  option = 'EQ'.
    IF sy-subrc EQ 0.
      max_date = range_line-low.
    ENDIF.

  ENDMETHOD.


  METHOD convert_range_single_values.

    LOOP AT dates_range
         REFERENCE INTO DATA(lo_line_range).
      IF lo_line_range->option EQ 'BT'.
        DATA(calculated_date) = lo_line_range->low.
        DO.
          APPEND VALUE trgs_datum( sign   = lo_line_range->sign
                                   option = 'EQ'
                                   low    = calculated_date      ) TO dates_range_simplified.
          calculated_date = calculated_date + 1.
          IF calculated_date GT lo_line_range->high OR
             lo_line_range->high IS INITIAL.
            EXIT.
          ENDIF.
        ENDDO.
      ELSE.
        APPEND VALUE trgs_datum( sign   = lo_line_range->sign
                                 option = lo_line_range->option
                                 low    = lo_line_range->low      ) TO dates_range_simplified.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

and the corresponding ABAP Unit, it is easy to test your example. ( you just have to do a CTRL + SHIFT + F10 to run it).

CLASS ltc_date_calculation DEFINITION
      FOR TESTING
      RISK LEVEL HARMLESS
      DURATION SHORT
      FINAL.

  PRIVATE SECTION.
    METHODS setup.
    METHODS check_convert_range_is_ok FOR TESTING.
    METHODS check_max_date_exampl_1   FOR TESTING.
    METHODS check_max_date_exampl_2   FOR TESTING.
    METHODS check_max_date_exampl_3   FOR TESTING.

    DATA o_cut TYPE REF TO lc_date_calculation.

ENDCLASS.


CLASS ltc_date_calculation IMPLEMENTATION.

  METHOD setup.
    o_cut = NEW #( ).
  ENDMETHOD.


  METHOD check_convert_range_is_ok.
    " Given I have a strange example
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'BT'  low = '20200101' high = '20200103' )
                                            ( sign = 'I'  option = 'NE'  low = '20200105' ) ).
    " When I call the transform method
    DATA(my_result_range) = o_cut->convert_range_single_values( my_input_range ).
    " Then I should have 4 entries in the result table with good entries
    cl_abap_unit_assert=>assert_equals(
      act = lines( my_result_range )
      exp = 4
      msg = 'You should have 4 lines in the result !' ).
    cl_abap_unit_assert=>assert_table_contains(
      table = my_result_range
      line  = VALUE trgs_datum( sign = 'I' option = 'EQ' low = '20200102'  ) ).
    cl_abap_unit_assert=>assert_table_contains(
      table = my_result_range
      line  = VALUE trgs_datum( sign = 'I' option = 'NE' low = '20200105'  ) ).
  ENDMETHOD.


  METHOD check_max_date_exampl_1.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'GT'  low = '20200220'  ) ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '99991231' ).
  ENDMETHOD.


  METHOD check_max_date_exampl_2.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'BT'  low = '20200220' high = '20201231' )
                                            ( sign = 'E'  option = 'EQ'  low = '20200930'                   )
                                            ( sign = 'I'  option = 'BT'  low = '20190101' high = '20191231' )
                                            ( sign = 'I'  option = 'LE'  low = '20160101'                   )  ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '20201231' ).
  ENDMETHOD.



METHOD check_max_date_exampl_3.
    " Given I have the Guido example 1
    DATA(my_input_range) = VALUE trgr_date( ( sign = 'E'  option = 'BT'  low = '20200220' high = '20201231' )
                                            ( sign = 'E'  option = 'EQ'  low = '20200930'                   )
                                            ( sign = 'I'  option = 'BT'  low = '20190101' high = '20191231' )
                                            ( sign = 'I'  option = 'LE'  low = '20160101'                   )  ).
    " When I call the Max finder
    DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
    " Then the result should be ...
    cl_abap_unit_assert=>assert_equals(
      act = max_value
      exp = '20191231' ).
  ENDMETHOD.


ENDCLASS.

I think the code is not complete, there were examples not working.

I manage I GT Date_1 & E GT Date_2 but you could have other strange case just E GT Date ...

Nice effort! If OPs could provide test cases this way, that would avoid us, volunteers, losing so much time 🙂

There was a thread one time of someone trying to create a process: Local Dev lead create the Abap Unit, OffShore dev code until the Abap Unit works. It could be amazing to have this working

I agree. I think it's the concept of Extreme Programming, with Behavior Driven Development (given, when, then) A.K.A. Acceptance Test Driven Development and other names

matt
Active Contributor
0 Kudos

Not bad. (That's Yorkshire for "I'm impressed"). But there's a mixture of prefix and non-prefix notation, I prefer INSERT to APPEND, as it won't dump if the table type changes, and a bit too much white space. 😉

matthew.billingham, it was specifically for you I try to remove my Hungarian notation 🙂

Sandra_Rossi
Active Contributor

Thanks to the efforts of Frederic for providing a test class (if the OPs could always do that, it would be a marvelous world), I could find a simplest algorithm, and I added another test case (one of those "strange" ones that Frederic mentioned):

Additional test method:

METHODS high_values_excluded FOR TESTING.

METHOD high_values_excluded.
  DATA(my_input_range) = VALUE trgr_date( ( sign = 'I'  option = 'BT'  low = '20200220' high = '20201231' )
                                          ( sign = 'E'  option = 'BT'  low = '20201230' high = '20201231' ) ).
  DATA(max_value) = o_cut->get_max_date_from_range( my_input_range ).
  cl_abap_unit_assert=>assert_equals(
    act = max_value
    exp = '20201229' ).
ENDMETHOD.

Shorter code, which works for all test cases:

CLASS lc_date_calculation DEFINITION.
  PUBLIC SECTION.
    METHODS get_max_date_from_range
      IMPORTING
        dates_range     TYPE trgr_date
      RETURNING
        VALUE(max_date) TYPE sydatum.
ENDCLASS.
CLASS lc_date_calculation IMPLEMENTATION.
  METHOD get_max_date_from_range.
    DATA dates TYPE SORTED TABLE OF d WITH NON-UNIQUE KEY table_line.
    LOOP AT dates_range REFERENCE INTO DATA(lr_line_range).
      IF lr_line_range->low IS NOT INITIAL.
        COLLECT lr_line_range->low INTO dates.
        COLLECT CONV d( lr_line_range->low - 1 ) INTO dates.
        COLLECT CONV d( lr_line_range->low + 1 ) INTO dates.
      ENDIF.
      IF lr_line_range->high IS NOT INITIAL.
        COLLECT lr_line_range->high INTO dates.
        COLLECT CONV d( lr_line_range->high - 1 ) INTO dates.
        COLLECT CONV d( lr_line_range->high + 1 ) INTO dates.
      ENDIF.
    ENDLOOP.
    COLLECT CONV d( '99991231' ) INTO dates.
    DELETE dates WHERE table_line NOT IN dates_range.
    max_date = dates[ lines( dates ) ].
  ENDMETHOD.
ENDCLASS.

0 Kudos

When I wrote my test, I wonder what was the result for SAP, So I do the real test with SE16 transaction.

it could be so complex quickly

(I think you could refactor your code, we see quickly two times the same statements)

matt
Active Contributor

I've never felt that select-options are appropriate for dates... https://blogs.sap.com/2014/02/07/dates-and-select-options/

deca
Explorer

Hello,

brilliant solution!

Thank you very much for your replies.

I apologize for my late answer, due to coronavirus. I'm well (at least till now), but after my first post I had to face some problems bigger than finding max value in a data range.

Thank you again!

Best regards,

Guido