02-20-2020 5:18 PM
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
02-21-2020 7:04 AM
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 ...
02-20-2020 8:23 PM
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.
02-21-2020 7:04 AM
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 ...
02-21-2020 10:58 AM
Nice effort! If OPs could provide test cases this way, that would avoid us, volunteers, losing so much time 🙂
02-21-2020 11:50 AM
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
02-21-2020 12:03 PM
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
02-26-2020 2:35 PM
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. 😉
02-26-2020 3:03 PM
matthew.billingham, it was specifically for you I try to remove my Hungarian notation 🙂
02-21-2020 10:56 AM
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.
02-21-2020 1:27 PM
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)
02-21-2020 1:14 PM
I've never felt that select-options are appropriate for dates... https://blogs.sap.com/2014/02/07/dates-and-select-options/
02-26-2020 10:13 AM
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