How to compare structure values component-wise and display the differences?
Now I do it in a very primitive way:
DATA: tkomp TYPE komp,
tkomp2 TYPE komp.
WRITE: `Field differences: `.
DO 500 TIMES.
ASSIGN COMPONENT sy-index OF STRUCTURE tkomp TO FIELD-SYMBOL(<fld>).
IF sy-subrc = 0.
CHECK <fld> IS NOT INITIAL AND CONV string( <fld> ) CN ' 0,.'.
ENDIF.
ASSIGN COMPONENT sy-index OF STRUCTURE tkomp2 TO FIELD-SYMBOL(<fld2>).
IF sy-subrc <> 0.
EXIT.
ENDIF.
IF <fld> <> <fld2>.
WRITE: / `Component ` && sy-index && ` differs: ` && <fld>.
ENDIF.
ENDDO.
Maybe there is more beautiful way? Maybe there is something like CL_ABAP_CORRESPONDING or something newer?
I found oldie threads, where they say The Debugger uses the class CL_TPDA_TOOL_DIFF for analyzing differences, hence is my follow-ip question: is it something that we can achieve in debugger?
I've never seen an applet in ABAP debugger that allows comparing structures against each other.
Your solution is actually quite okay. You might want to add CL_ABAP_STRUCTDESCR to get the names of the components for nicer output. It would also enable you to compare and analyze the types of the component's fields as well.
Unfortunately, there is no reusable class, function, or built-in method for that.
You will find the most precise comparison implementation in the class CL_ABAP_UNIT_ASSERT, method ASSERT_EQUALS. More precisely, the local class DATA_DIFF, method DIFF_STRUCTS, shows how to compare structures in a type- and nesting-tolerant way.
I can't speak for the class CL_TPDA_TOOL_DIFF. I've heard it mentioned before but we actually do not even have it in our SAP NW 7.52 systems.
I have also never seen a debugger view or plugin that would compare structures and display the differences. No idea where that comment comes from.
There are code snippets at below, you can pass the structure in cl_abap_typedescr=>describe_by_data.
DATA: lo_struct TYPE REF TO cl_abap_structdescr,
lt_comp TYPE abap_component_tab,
ls_comp TYPE abap_componentdescr.
CLEAR lo_struct.
lo_struct ?= cl_abap_typedescr=>describe_by_data( p_data = lt_list_sum ).
REFRESH lt_comp.
lt_comp = lo_struct->get_components( ).
MOVE-CORRESPONDING gt_mov_grp TO gt_mov_grp_std.
LOOP AT lt_comp INTO ls_comp.
CLEAR lv_numeric.
IF ls_comp-name(4) EQ cx_move.
lv_numeric = ls_comp-name+5(2).
READ TABLE gt_mov_grp_std REFERENCE INTO lr_mov_grp WITH KEY report_order = lv_numeric.
IF sy-subrc EQ 0 AND
lr_mov_grp IS BOUND.
APPEND INITIAL LINE TO et_data REFERENCE INTO DATA(lr_data).
MOVE-CORRESPONDING lr_mov_grp->* TO lr_data->*.
IF <fs> IS ASSIGNED.
UNASSIGN <fs>.
ENDIF.
ASSIGN COMPONENT ls_comp-name OF STRUCTURE lt_list_sum TO <fs>.
IF sy-subrc EQ 0 AND
<fs> IS ASSIGNED.
lr_data->amount = <fs>.
ENDIF.
IF <fs> IS ASSIGNED.
UNASSIGN <fs>.
ENDIF.
ASSIGN COMPONENT cx_currency OF STRUCTURE lt_list_sum TO <fs>.
IF sy-subrc EQ 0 AND
<fs> IS ASSIGNED.
lr_data->currency = <fs>.
ENDIF.
lr_data->datum = iv_datum.
lr_data->werks = iv_werks.
lr_data->kunnr = iv_kunnr.
ENDIF.
ENDIF.
ENDLOOP.
Related
I'm currently trying to perform a dynamic lossless assignment in an ABAP 7.0v SP26 environment.
Background:
I want to read in a csv file and move it into an internal structure without any data losses. Therefore, I declared the field-symbols:
<lfs_field> TYPE any which represents a structure component
<lfs_element> TYPE string which holds a csv value
Approach:
My current "solution" is this (lo_field is an element description of <lfs_field>):
IF STRLEN( <lfs_element> ) > lo_field->output_length.
RAISE EXCEPTION TYPE cx_sy_conversion_data_loss.
ENDIF.
I don't know how precisely it works, but seems to catch the most obvious cases.
Attempts:
MOVE EXACT <lfs_field> TO <lfs_element>.
...gives me...
Unable to interpret "EXACT". Possible causes: Incorrect spelling or comma error
...while...
COMPUTE EXACT <lfs_field> = <lfs_element>.
...results in...
Incorrect statement: "=" missing .
As the ABAP version is too old I also cannot use EXACT #( ... )
Example:
In this case I'm using normal variables. Lets just pretend they are field-symbols:
DATA: lw_element TYPE string VALUE '10121212212.1256',
lw_field TYPE p DECIMALS 2.
lw_field = lw_element.
* lw_field now contains 10121212212.13 without any notice about the precision loss
So, how would I do a perfect valid lossless assignment with field-symbols?
Don't see an easy way around that. Guess that's why they introduced MOVE EXACT in the first place.
Note that output_length is not a clean solution. For example, string always has output_length 0, but will of course be able to hold a CHAR3 with output_length 3.
Three ideas how you could go about your question:
Parse and compare types. Parse the source field to detect format and length, e.g. "character-like", "60 places". Then get an element descriptor for the target field and check whether the source fits into the target. Don't think it makes sense to start collecting the possibly large CASEs for this here. If you have access to a newer ABAP, you could try generating a large test data set there and use it to reverse-engineer the compatibility rules from MOVE EXACT.
Back-and-forth conversion. Move the value from source to target and back and see whether it changes. If it changes, the fields aren't compatible. This is unprecise, as some formats will change although the values remain the same; for example, -42 could change to 42-, although this is the same in ABAP.
To-longer conversion. Move the field from source to target. Then construct a slightly longer version of target, and move source also there. If the two targets are identical, the fields are compatible. This fails at the boundaries, i.e. if it's not possible to construct a slightly-longer version, e.g. because the maximum number of decimal places of a P field is reached.
DATA target TYPE char3.
DATA source TYPE string VALUE `123.5`.
DATA(lo_target) = CAST cl_abap_elemdescr( cl_abap_elemdescr=>describe_by_data( target ) ).
DATA(lo_longer) = cl_abap_elemdescr=>get_by_kind(
p_type_kind = lo_target->type_kind
p_length = lo_target->length + 1
p_decimals = lo_target->decimals + 1 ).
DATA lv_longer TYPE REF TO data.
CREATE DATA lv_longer TYPE HANDLE lo_longer.
ASSIGN lv_longer->* TO FIELD-SYMBOL(<longer>).
<longer> = source.
target = source.
IF <longer> = target.
WRITE `Fits`.
ELSE.
WRITE `Doesn't fit, ` && target && ` is different from ` && <longer>.
ENDIF.
Is there any Function module that can generate ABAP code.
For eg: FM takes tables name and join conditions as input and generate ABAP code corresponding to that.
Thanks
You should consider using SAPQuery. SAP documentation here: https://help.sap.com/saphelp_erp60_sp/helpdata/en/d2/cb3efb455611d189710000e8322d00/content.htm
1. Generic reports are possible.
Your problem is, that You will have to draw a strict frame of what is
generic and what not, this means, some stuff MUST be that generic, that
it will deal with WHATEVER You want to do before ( mostly the selection ) ,
during ( mostly manipulation ---> I would offer a badi for that ), and output.
This means, that there is at least the output-step, which can be valid for ALL
data resulting from the steps before.
Consider a generic ALV-table_output, there are a lot of examples in the repo.
If You want to be the stuff printed out simple as list, this might include
more work, like, how big is the structure, when Dou You wrap a line, and so on, consider using a flag which allows to toggle the type of output .
2. Generic reports are a transportable object.
This refers to point one. Define clear stages and limits. What does the report do, and what is it not able to do. Because, even if it is in customer's namespace, each modification still will be put into transport-layers. Therefore a strict definition of features/limits is necessary so that the amount of transports due to "oh, but we also need that"-statements will not become infinite.
2. Generic reports are strict.
What does that mean ? You might want to parse the passed data ( table names, join-binding, selection-parameter-values ) and throw exceptions, if not properly set. Much work. You should offer a badi for that. If You do not do this, expect a dump. let it dump. In the end the user of Your report-api should know ( by documentation perhaps) how to call it. If not, a dynamic SQL-dump will be the result.
3. Generic reports might benefit from badis/exits.
This is self explanaining, I think. Especially generic/dynamic selection/modification/displaying of data should be extendable in terms of
custom-modifications. When You inspect, what a f4-search-help exit works like, You will understand, what I mean.
4. Generic coding is hard to debug, mostly a blackbox.
Self explaining, in the code-section below I can mark some of those sections.
5. Generic coding has some best prectice examples in the repo.
Do not reinvent the wheel. Check, how the se16n works by debugging it,
check how se11 works by debugging it. Check, what the SQL-Query-builder
looks like in the debugger. You will get the idea very soon,
and the copy-paste should be the most simple part of Your work.
6. That are the basic parts of what You might use.
Where clause determination and setting the params.
data lt_range type rsds_trange.
data ls_range_f type rsds_frange.
data lt_where type rsds_twhere.
data ls_where like line of lt_where.
ls_range_f = value #( sign = _sign
option = _option
low = _low
high = _high ).
.
.
.
append ls_frange to lt_range.
.
.
.
call function 'FREE_SELECTIONS_RANGE_2_WHERE'
exporting
field_ranges = lt_range
importing
where_clauses = lt_where.
You have the parameter, let us create the select-result-table.
data(lt_key) = value abap_keydescr_tab( for line in _joinfields)
( name = fieldname ) ).
data(lo_structdescr) = cast cl_abap_structdescr( cl_abap_structdescr=>describe_by_name( _struct_name ) ).
data(lo_tabledescr) = cl_abap_tabledescr=>create( line_type = lo_structdescr p_key = lt_key ).
create data ro_data type handle lo_tabledescr.
.
.
.
select (sel_st)
from (sel_bind)
into corresponding fields of table t_data
where (dyn_where).
Then assign the seelct-table-result-reference to the generic table of this select.
Do You need more hints ?
Yes, such possibility exists, but not by means of function modules. INSERT REPORT statement allows generating report by populating its code from internal text table:
INSERT REPORT prog FROM itab
[MAXIMUM WIDTH INTO wid]
{ [KEEPING DIRECTORY ENTRY]
| { [PROGRAM TYPE pt]
[FIXED-POINT ARITHMETIC fp]
[UNICODE ENABLING uc] }
| [DIRECTORY ENTRY dir] }.
Is there any way of programmatically getting the value of a Text Symbol at runtime?
The scenario is that I have a simple report that calls a function module. I receive an exported parameter in variable LV_MSG of type CHAR1. This indicates a certain status message created in the program, for instance F (Fail), X (Match) or E (Error). I currently use a CASE statement to switch on LV_MSG and fill another variable with a short description of the message. These descriptions are maintained as text symbols that I retrieve at compile time with text-MS# where # is the same as the possible returns of LV_MSG, for instance text-MSX has the value "Exact Match Found".
Now it seems to me that the entire CASE statement is unnecessary as I could just assign to my description variable the value of the text symbol with ID 'MS' + LV_MSG (pseudocode, would use CONCATENATE). Now my issue is how I can find a text symbol based on the String representation of its ID at runtime. Is this even possible?
If it is, my code would look cleaner and I wouldn't have to update my actual code when new messages are added in the function module, as I would simply have to add a new text symbol. But would this approach be any faster or would it in fact degrade the report's performance?
Personally, I would probably define a domain and use the fixed values of the domain to represent the values. This way, you would even get around the string concatenation. You can use the function module DD_DOMVALUE_TEXT_GET to easily access the language-dependent text of a domain value.
To access the text elements of a program, use a function module like READ_TEXT_ELEMENTS.
Be aware that generic programming like this will definitely slow down your program. Whether it would make your code look cleaner is in the eye of the beholder - if the values change rarely, I don't see why a simple CASE statement should be inferior to some generic text access.
Hope I understand you correctly but here goes. This is possible with a little trickery, all the text symbols in a report are defined as variables in the program (with the name text-abc where abc is the text ID). So you can use the following:
data: lt_all_text type standard table of textpool with default key,
lsr_text type ref to textpool.
"Load texts - you will only want to do this once
read textpool sy-repid into lt_all_text language sy-langu.
sort lt_all_Text by entry.
"Find a text, the field KEY is the text ID without TEXT-
read table lt_all_text with key entry = i_wanted_text
reference into lsr_text binary search.
If you want the address you can add:
field-symbols: <l_text> type any.
data l_name type string.
data lr_address type ref to data.
concatenate 'TEXT-' lsr_text->key into l_name.
assign (l_name) to <l_text>.
if sy-subrc = 0.
get reference of <l_text> into lr_address.
endif.
As vwegert pointed out this is probably not the best solution, for error handling rather use message classes or exception objects. This is useful in other cases though so now you know how.
I have a deep structure that I would like to display as a tree with the values of each field (sort of like the hierarchical display of a structure you can do in SE11, but with values).
Is there a class or function that does this for you? I really don't want to have to go reinvent the wheel.
Well, I would say it is faster to do DIY then to search for something generic enough to help you. You can try following coding as basis.
It does the plain recursion through the variable (be it table or structure) and it prints the fields found at bottom...
*&---------------------------------------------------------------------*
*& Form print_structure
*&---------------------------------------------------------------------*
form print_structure using im_data.
data: lr_typeref type ref to cl_abap_typedescr,
lf_ddic_in type fieldname,
lt_dfies type ddfields,
lf_string type c length 200.
field-symbols: <lt_table> type any table,
<ls_table> type any,
<lf_field> type any,
<ls_dfies> like line of lt_dfies.
lr_typeref = cl_abap_typedescr=>describe_by_data( im_data ).
case lr_typeref->type_kind.
when cl_abap_typedescr=>typekind_table. " internal table
assign im_data to <lt_table>.
loop at <lt_table> assigning <ls_table>.
perform print_structure using <ls_table>.
endloop.
when cl_abap_typedescr=>typekind_struct1 or
cl_abap_typedescr=>typekind_struct2. " deep/flat structure
lf_ddic_in = lr_typeref->get_relative_name( ).
call function 'DDIF_FIELDINFO_GET'
exporting
tabname = lf_ddic_in
all_types = 'X'
tables
dfies_tab = lt_dfies
exceptions
not_found = 1
others = 0.
check sy-subrc eq 0.
loop at lt_dfies assigning <ls_dfies>.
assign component <ls_dfies>-fieldname of structure im_data to <lf_field>.
perform print_structure using <lf_field>.
endloop.
when others. " any field
write im_data to lf_string.
write: / lf_string.
endcase.
endform. "print_structure
Would an ALV Tree work? CL_SALV_TREE
I've never seen such functionality and think there is no one in standard. Can't remeber any situation in standard where such functionality should be used. In my opinion most appropriate way to implement this - to use Column Tree. Take a look into SAPCOLUMN_TREE_CONTROL_DEMO
In algebra if I make the statement x + y = 3, the variables I used will hold the values either 2 and 1 or 1 and 2. I know that assignment in programming is not the same thing, but I got to wondering. If I wanted to represent the value of, say, a quantumly weird particle, I would want my variable to have two values at the same time and to have it resolve into one or the other later. Or maybe I'm just dreaming?
Is it possible to say something like i = 3 or 2;?
This is one of the features planned for Perl 6 (junctions), with syntax that should look like my $a = 1|2|3;
If ever implemented, it would work intuitively, like $a==1 being true at the same time as $a==2. Also, for example, $a+1 would give you a value of 2|3|4.
This feature is actually available in Perl5 as well through Perl6::Junction and Quantum::Superpositions modules, but without the syntax sugar (through 'functions' all and any).
At least for comparison (b < any(1,2,3)) it was also available in Microsoft Cω experimental language, however it was not documented anywhere (I just tried it when I was looking at Cω and it just worked).
You can't do this with native types, but there's nothing stopping you from creating a variable object (presuming you are using an OO language) which has a range of values or even a probability density function rather than an actual value.
You will also need to define all the mathematical operators between your variables and your variables and native scalars. Same goes for the equality and assignment operators.
numpy arrays do something similar for vectors and matrices.
That's also the kind of thing you can do in Prolog. You define rules that constraint your variables and then let Prolog resolve them ...
It takes some time to get used to it, but it is wonderful for certain problems once you know how to use it ...
Damien Conways Quantum::Superpositions might do what you want,
https://metacpan.org/pod/Quantum::Superpositions
You might need your crack-pipe however.
What you're asking seems to be how to implement a Fuzzy Logic system. These have been around for some time and you can undoubtedly pick up a library for the common programming languages quite easily.
You could use a struct and handle the operations manualy. Otherwise, no a variable only has 1 value at a time.
A variable is nothing more than an address into memory. That means a variable describes exactly one place in memory (length depending on the type). So as long as we have no "quantum memory" (and we dont have it, and it doesnt look like we will have it in near future), the answer is a NO.
If you want to program and to modell this behaviour, your way would be to use a an array (with length equal to the number of max. multiple values). With this comes the increased runtime, hence the computations must be done on each of the values (e.g. x+y, must compute with 2 different values x1+y1, x2+y2, x1+y2 and x2+y1).
In Perl , you can .
If you use Scalar::Util , you can have a var take 2 values . One if it's used in string context , and another if it's used in a numerical context .