Use dynamic structure field in ABAP - structure

I have a variable which should determine which field of a structure I use in further compilations.
So assume my variable is named currency and can have the values 'K', 'T' and 'H'. After checking which value it is, I want to use corresponding structure fields, e.g. mystructure-fieldk, mystructure-fieldt, mystructure-fieldi.
As for now, I just use IF..THEN to check the value of my variable currency and then just use the corresponding field of the structure, but therefore I have to repeat the coding of my loop 3 times which is just ugly.
LOOP AT itab ASSIGNING <fs>.
mystructure-fieldk = mystructure-fieldk + <fs>-otherfieldk.
ENDLOOP.
Is there a way that i can use the loop with a dynamic field? As in:
IF currency = 'K'.
DATA(mydynamicfield) = fieldk.
ENDIF.
and then use mydynamicfield in the loop:
LOOP AT itab ASSIGNING <fs>.
mystructure-mydynamicfield = mystructure-mydynamicfield + <fs>-otherfieldk.
ENDLOOP.
Or anything else? Thanks a lot!

Use the below piece of code
LOOP AT ITAB ASSIGNING <fs-structure>.
*-- determination of field name logic goes here let's say you have
*-- field name is in variable lv_field
ASSIGN COMPONENT (lv_field) of STRUCTURE <fs-structure> to <fs-field>.
IF <fs-field> IS ASSIGNED.
<fs-field> = 'the value you want to assign'.
ENDIF.
ENDLOOP.
Hope this clarifies.
Let's say you want to compute based on other field in the same structure
LOOP AT ITAB ASSIGNING <fs-structure>.
*-- determination of field name logic goes here let's say you have
*-- field name is in variable lv_field1 and other is lv_field2
ASSIGN COMPONENT (lv_field1) of STRUCTURE <fs-structure> to <fs-field1>.
ASSIGN COMPONENT (lv_field2) of STRUCTURE <fs-structure> to <fs-field2>.
IF <fs-field1> IS ASSIGNED ANDV<fs-field2> IS ASSIGNED .
<fs-field1> = <fs-field1> + <fs-field1>.
ENDIF.
ENDLOOP.
Note: the code is written to give an idea on how to proceed further in this issue. As Sandra pointed out this may result in compile issues please correct as necessary. I tried answering the question issuing a mobile device only. I don't have access to a compiler/system currently.

There's a better performance with a static reference to the components:
TYPES: BEGIN OF ty_structure,
fieldk TYPE i,
fieldt TYPE i,
fieldh TYPE i,
END OF ty_structure.
DATA: itab TYPE TABLE OF ty_structure,
mystructure TYPE ty_structure,
currency TYPE c LENGTH 1.
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>).
CASE currency.
WHEN 'K'. ADD <fs>-fieldk TO mystructure-fieldk.
WHEN 'T'. ADD <fs>-fieldt TO mystructure-fieldt.
WHEN 'H'. ADD <fs>-fieldh TO mystructure-fieldh.
ENDCASE.
ENDLOOP.
If you want to simplify the code, you may use a macro (inline code):
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>).
DEFINE m_add.
IF currency = 'K'.
ADD <fs>-field&1 TO mystructure-field&1.
ENDIF.
END-OF-DEFINITION.
m_add : K, T, H.
ENDLOOP.

I created working example with data. I believe, this will definitely help you.
dynamic field values are calculated and result is displayed at the end.
TYPES: BEGIN OF ty_itab,
currency TYPE c,
fieldk TYPE i,
fieldt TYPE i,
fieldh TYPE i,
END OF ty_itab.
TYPES: BEGIN OF ty_structure,
fieldk TYPE i,
fieldt TYPE i,
fieldh TYPE i,
END OF ty_structure.
DATA: itab TYPE TABLE OF ty_itab,
ls_structure TYPE ty_structure.
itab = VALUE #( ( currency = 'H' fieldk = 10 fieldt = 20 fieldh = 30 )
( currency = 'H' fieldk = 50 fieldt = 60 fieldh = 70 )
( currency = 'T' fieldk = 100 fieldt = 120 fieldh = 150 )
( currency = 'K' fieldk = 300 fieldt = 400 fieldh = 500 ) ).
LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>).
CASE <fs>-currency.
WHEN 'K'. ADD <fs>-fieldk TO ls_structure-fieldk.
WHEN 'T'. ADD <fs>-fieldt TO ls_structure-fieldt.
WHEN 'H'. ADD <fs>-fieldh TO ls_structure-fieldh.
ENDCASE.
ENDLOOP.
cl_demo_output=>display( ls_structure ).

You can try with below code:
TYPES: BEGIN OF ts_basic_line,
matnr TYPE mara-matnr,
lifnr TYPE lfa1-lifnr,
END OF ts_basic_line,
ty_basic_line TYPE TABLE OF ts_basic_line.
DATA: lt_basic_data TYPE ty_basic_line,
lo_structure TYPE REF TO cl_abap_structdescr,
lt_components TYPE abap_component_tab.
lt_basic_data = VALUE #( ( matnr = '111' lifnr = '333' ) ). " data for test
READ TABLE lt_basic_data ASSIGNING FIELD-SYMBOL(<fs_line>) INDEX 1.
IF sy-subrc EQ 0.
lo_structure ?= cl_abap_typedescr=>describe_by_data( <fs_line> ).
lt_components = lo_structure->get_components( ).
LOOP AT lt_components ASSIGNING FIELD-SYMBOL(<fs_strucutre_fields>).
LOOP AT lt_basic_data ASSIGNING FIELD-SYMBOL(<fs_data>).
ASSIGN COMPONENT <fs_strucutre_fields>-name OF STRUCTURE <fs_data> TO FIELD-SYMBOL(<fs_value>).
IF sy-subrc EQ 0.
" here your code, e.x. data conversion
<fs_value> = |{ <fs_value> ALPHA = IN }|.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDIF.

Related

How to count the words of a string without using any function in abap

Hello Experts
I need a help to count the words of string without using any function in ABAP.
We can only use do loop and if condition.
Please Help
METHODS count_words
IMPORTING
iv_text TYPE string
RETURNING
VALUE(rv_result) TYPE i.
METHOD count_words.
CONSTANTS lc_space TYPE string VALUE ` `.
DATA(lv_remaining_text) = iv_text.
DATA(lv_last_char) = ``.
DO.
IF lv_remaining_text IS INITIAL.
RETURN. " or EXIT if you omit the METHOD around this
ENDIF.
DATA(lv_next_char) = lv_remaining_text(1).
IF lv_next_char <> lc_space AND
( lv_last_char IS INITIAL OR
lv_last_char = lc_space ).
rv_result = rv_result + 1.
ENDIF.
lv_last_char = lv_next_char.
lv_remaining_text = lv_remaining_text+1.
ENDDO.
ENDMETHOD.
Please don't code like this in practice. This sort of low-level character juggling is only for educational purposes.
In real life, please use something like this:
METHOD count_words_cleanly.
rv_result = count( val = iv_text
regex = `(\s\S|^\S)` ).
ENDMETHOD.
You could count the number of spaces between your words.
You need at least STRLEN to determine the string length... without it I dont think this works. CONDENSE might be useful in some cases... give it a try without CONDENSE.
DATA a TYPE string VALUE 'MY NAME IS JOHN'.
DATA c TYPE i.
DATA d TYPE c.
DATA words TYPE i value 1.
DATA e TYPE i VALUE 0.
CONDENSE a.
c = STRLEN( a ).
DO c TIMES.
d = a+e(1).
e = e + 1.
IF d = ' '.
words = words + 1.
ENDIF.
ENDDO.
WRITE words. // just as output ... delete on demand

How to do typecasting without loosing a comma?

Task: I have data col(30) TYPE c VALUE '-1111,45'. and I need to check if this value is negative, if negative - do typecasting. But on the output I get 5 though I have to get 1111,45 without a minus and again do typecasting to the previous type (с).
REPORT Z_CAST.
data col(30) TYPE c VALUE '-1111,45'.
data numc type n.
numc = col.
if numc < 0.
numc = -1 * numc.
endif.
col = numc.
WRITE col. "Output: 5
REPORT Z_CAST.
data col(30) TYPE c VALUE '-1111,45'.
if col+0(1) EQ '-'.
WRITE col+1. "1111,45
endif.
This is the minimum character processing way to do this:
col = replace( val = col
sub = '-'
with = ' ' ).
You don't have to find the - first to replace it.
And this is the clean numeric way that ABAPers will usually choose:
DATA n TYPE p LENGTH 12 DECIMALS 2.
n = col.
n = abs( n ).
Note that this does not work with your example '-1111,45' though: ABAP expects a point . as decimal separator, but your number uses a localized format with comma ,, so you would have to normalize the number first.

Cleaning empty cells in internal table

I'm trying to clean the following empty cells marked in red from this internal table before I display it in an ALV.
If a cell is found to be blank, look for any cells underneath that have value and move up.
I am struggling to figure out what is the best way in code to perform this.
Any help would be great.
It is undoubtedly that something is wrong with your merging logic, however your task is quite interesting and this is one of the possible ways it can be solved.
I took your structure and made an assumption that none of the rows in your table is fully filled, i.e. either first three columns are filled (struct_left) or last three (struct_right). This is how I feel it from your screenshots.
REPORT z_sections.
TYPES:
BEGIN OF struct_left, " left structure
LEFTDAMAGED TYPE c LENGTH 1,
LEFTDAMAGEDDESC TYPE c LENGTH 3,
LEFTDAMAGEDDESCT TYPE c LENGTH 30,
END OF struct_left,
BEGIN OF struct_right, " right structure
RIGHTDAMAGED TYPE c LENGTH 1,
RIGHTDAMAGEDDESC TYPE c LENGTH 3,
RIGHTDAMAGEDDESCT TYPE c LENGTH 30,
END OF STRUCT_right.
TYPES BEGIN OF ty_table.
INCLUDE TYPE struct_left.
INCLUDE TYPE struct_right.
TYPES END OF ty_table.
DATA: lt_current_table TYPE TABLE OF ty_table INITIAL SIZE 100,
ls_current_table LIKE LINE OF lt_current_table,
i TYPE i.
FIELD-SYMBOLS: <fld> TYPE clike.
DATA: r_random TYPE REF TO cl_abap_random_packed,
seed TYPE i.
seed = cl_abap_random=>seed( ).
CALL METHOD cl_abap_random_packed=>create
EXPORTING
seed = seed
min = -999999999999999
max = 999999999999999
RECEIVING
prng = r_random.
DEFINE randomize. " filling row with random data
ASSIGN COMPONENT &1 OF STRUCTURE &2 TO <fld>.
<fld> = r_random->get_next( ).
&1 = &1 + 1.
ASSIGN COMPONENT &1 OF STRUCTURE &2 TO <fld>.
<fld> = r_random->get_next( ).
&1 = &1 + 1.
ASSIGN COMPONENT &1 OF STRUCTURE &2 TO <fld>.
<fld> = r_random->get_next( ).
END-OF-DEFINITION.
START-OF-SELECTION.
* filling table with random stuff
DO 100 TIMES.
CLEAR ls_current_table.
IF sy-index MOD 3 = 0.
i = 1.
randomize i ls_current_table.
ELSE.
i = 4.
randomize i ls_current_table.
ENDIF.
APPEND ls_current_table TO lt_current_table.
ENDDO.
DATA: ls_left TYPE struct_left,
ls_right TYPE struct_right.
DATA lt_new LIKE lt_current_table.
* collapsing table
LOOP AT lt_current_table ASSIGNING FIELD-SYMBOL(<fs_current>) WHERE leftdamaged IS NOT INITIAL.
DELETE lt_current_table WHERE leftdamaged IS INITIAL AND leftdamageddesc IS INITIAL AND leftdamageddesct IS INITIAL AND
rightdamaged IS INITIAL AND rightdamageddesc IS INITIAL AND rightdamageddesct IS INITIAL. " remove empty lines
MOVE-CORRESPONDING <fs_current> TO ls_left.
READ TABLE lt_current_table ASSIGNING FIELD-SYMBOL(<fs_right>) WITH KEY leftdamaged = ''.
IF <fs_right> IS ASSIGNED.
MOVE-CORRESPONDING <fs_right> TO ls_right.
CLEAR: <fs_right>.
ENDIF.
CLEAR: <fs_current>.
IF ls_left IS NOT INITIAL AND ls_right IS NOT INITIAL.
CLEAR: ls_current_table.
MOVE-CORRESPONDING ls_left TO ls_current_table.
MOVE-CORRESPONDING ls_right TO ls_current_table.
APPEND ls_current_table TO lt_new.
CLEAR: ls_left, ls_right.
ENDIF.
ENDLOOP.
You can sort the internal table and store it in a temp internal table, and swap them. For instance:
data: lt_itab_temp like table of lt_itab.
move lt_itab[] to lt_itab_temp[].
clear:lt_itab[],lt_itab.
sort lt_itab_temp descending by rightdamagedesc rightdamagedesct.
move lt_itab_temp[] to lt_itab[].
OR, you can loop through the fieldcatalog, set "no_display" or "no_out" field to 'X'.

Data didn't show in the first row and start to calculate from second (ABAP,BW)

Can you help me with ABAP code (I make some simple logic for BW).
When data is appended to the table, it calculates the first row, but writes the result in the second, second to third, etc. And on the first row I have zero result.
What can it be? Can you help me, please, maybe I lost something in my code.
My code:
FIELD-SYMBOLS: <ls_data> TYPE any,
<lv_plant> TYPE /BI0/OIPLANT.
TYPES: begin of ty_kpi_result,
plant type /BI0/OIPLANT,
mat_num_01(18) type n,
mat_num_02(18) type n,
END OF ty_kpi_result.
DATA: lt_result TYPE TABLE OF ty_kpi_result,
ls_result like LINE OF lt_result,
lv_count type i,
lv_plant_1 type /BI0/OIPLANT.
sort ct_data ASCENDING.
LOOP AT ct_data ASSIGNING <ls_data>.
ASSIGN COMPONENT 'PLANT' OF STRUCTURE <ls_data> to <lv_plant>.
* lv_count = lv_count + 1.
ls_result-plant = <lv_plant>.
IF lv_plant_1 = <lv_plant>.
lv_count = lv_count + 1.
ls_result-mat_num_01 = lv_count.
ELSE.
lv_count = 0.
lv_count = lv_count + 1.
APPEND ls_result to lt_result.
ENDIF.
lv_plant_1 = <lv_plant>.
ls_result-mat_num_01 = lv_count.
ENDLOOP.
Found solution on SAP forum:
FIELD-SYMBOLS: <ls_data> TYPE any,
<lv_plant> TYPE /bi0/oiplant.
TYPES: BEGIN OF ty_kpi_result,
plant TYPE /bi0/oiplant,
mat_num_01 TYPE i,
mat_num_02 TYPE i,
END OF ty_kpi_result.
DATA: lt_result TYPE sorted TABLE OF ty_kpi_result with UNIQUE KEY plant,
ls_result LIKE LINE OF lt_result,
lv_count TYPE i,
lv_plant_1 TYPE /bi0/oiplant.
SORT ct_data BY ('PLANT').
CLEAR ls_result.
LOOP AT ct_data ASSIGNING <ls_data>.
ASSIGN COMPONENT 'PLANT' OF STRUCTURE <ls_data> TO <lv_plant>.
IF <lv_plant> EQ ls_result-plant.
ADD 1 TO ls_result-mat_num_01.
ELSE.
IF ls_result-plant IS NOT INITIAL. " first record
APPEND ls_result TO lt_result.
ENDIF.
CLEAR ls_result.
ls_result-plant = <lv_plant>.
ls_result-mat_num_01 = 1.
ENDIF.
AT LAST. " last record
APPEND ls_result TO lt_result.
ENDAT.
ENDLOOP.
ENDMETHOD.

How to split a series of number and compare it with another set of digit?

Say I have a no. 20101105, I need to compare it with a series of other nos. say 20110105 , 20090105 and find the nearest no. of it.
I don't want to compare it on the whole, I need to compare it each digit wise by parsing thru it and then see which is the closest.
Can someone suggest on how to do this in ABAP language?
In general You should mention some more information. For example, are the numbers really integers ? Then You can put them into an internal table and sorting all of them is the easiest solution to find any "nearest" number relating to an actual scanned. This is just like integers work in sort, they are sorted like numbers, my friend. But If You want it character-wise ( what really makes no sense, if the numbers are integers ) i give You some help with this character-comparison in a do-loop, taking smaller string-length as iterator-counter. I omitted the else, that's Your "homework". :-D
DATA:
lv_length1 TYPE i,
lv_length2 TYPE i,
lv_cnt TYPE i,
lv_teststr1 TYPE string VALUE '123456',
lv_teststr2 TYPE string VALUE '1235'.
lv_length1 = strlen( lv_teststr1 ).
lv_length2 = strlen( lv_teststr2 ).
IF lv_length1 GE lv_length2.
DO lv_length2 TIMES.
IF lv_teststr2+lv_cnt(1) NE lv_teststr1+lv_cnt(1).
BREAK-POINT.
ENDIF.
ADD 1 TO lv_cnt.
ENDDO.
ENDIF.
The counter variable is also the index of, in this case, the first not matching character. This gets the job done.
Coded and tested by me just right now.
I don't know if I understood but maybe this helps.
report znearest.
data lv_value(8) type n.
parameters p_value(8) type n. " ---------> The value
select-options s_values for lv_value. " -> The list
start-of-selection.
data: wa like line of s_values,
lv_dif(8) type n,
lv_nearest(8) type n,
lv_nearest_dif(8) type n,
lv_first type c.
loop at s_values into wa.
lv_dif = abs( p_value - wa-low ). " Calculate the difference
if lv_first is initial.
lv_nearest_dif = lv_dif.
lv_first = 'X'.
endif.
if lv_dif le lv_nearest_dif. " Compare the differences
lv_nearest = wa-low.
lv_nearest_dif = lv_dif.
endif.
endloop.
write: 'The nearest from', p_value, 'is', lv_nearest.
Hope it helps.