Concatenate using 'FOR' loop in VALUE operator with 'BASE' addition? - abap

Is it possible to mix in a FOR with CONCATENATE along with BASE statement?
Normally, itab1 = VALUE #( BASE itab1 ( value1 ) ) will append line1 into itab1 without overwriting. Shouldn't it be the same when using FOR along with BASE?
This is my thought process, but I get the No component exists with the name "FOR" error:
itab1 =
VALUE #(
BASE itab1
( value1 && value2 )
( VALUE #(
FOR line in itab2
( line-fld1 && line-fld2 ) )
).

Shouldn't it be the same when using FOR along with BASE
Yes, the semantics is the same. You use BASE for preserving the rows of the LHS when adding rows of RHS, and line specification is the same:
itab1 = VALUE #( BASE itab1 FOR line in itab2 ( matnr = line-matnr
maktx = line-matnr && line-spras
spras = line-spras ) ).
However, there is a nuance you should remember here: you cannot put the same itab to the inline-declared LHS, to the BASE source and to the FOR loop, it will give you infinite loop and TSV_TNEW_PAGE_ALLOC_FAILED dump.

I have tried out what Sandra had suggested in the comments and it worked like a charm:
You may append the lines of an internal table only by using ( LINES OF
itab ), so in your case it should be ( LINES OF VALUE #( ... ) ) –
Sandra Rossi
itab1 =
VALUE #( BASE itab1 ( value1 && value2 )
( LINES OF VALUE #( FOR line in itab2 ( line-fld1 && line-fld2 ) ) ).

Related

Best way to check if a line with non-initial field exists?

Let's say I have a table quants and want to find out if any line exists, where the field lenum is not initial. The table is declared inline using a select statement, so I do not have a key available.
Because I don't have a key, the following solution does not work:
line_exists( VALUE #( FOR wa IN quants WHERE ( lenum IS NOT INITIAL ) ( wa ) ) )
Since I want to check for inequality, a table expression does not work:
line_exists( quants[ lenum NE '' ] )
The only solution that I have come up with so far is the following:
abap_true EQ REDUCE abap_bool( INIT bool = abap_false FOR quant IN quants WHERE ( lenum IS NOT INITIAL ) NEXT bool = abap_true )
Obviously there are "old fashioned" solutions, but is there any newer-style?
By "old fashioned" I mean solutions like this:
LOOP AT quants INTO DATA(wa).
IF wa-lenum IS INITIAL.
DATA(found) = abap_true.
ENDIF.
ENDLOOP.
IF found EQ abap_true.
...
ENDIF.
The only thin in "new fashion" would be SELECT, FROM #itab.
DATA(lv_exists) = abap_false.
SELECT SINGLE #abap_true FROM #lt_quant AS quant WHERE quant~lenum IS NOT INITIAL INTO #lv_exists.
See documentation link for performance impact (best case is handled like a table in the table buffer) and limitations (e.g. no string column).
The most performant and less restrictions would be this:
LOOP AT lt_quant TRANSPORTING NO FIELDS WHERE lenum IS NOT INITIAL.
EXIT.
ENDLOOP.
DATA(lv_exists) = xsdbool( sy-subrc = 0 ).

how to Filter on internal table using multiple fields

My requirement is to filter internal table using multiple fields.
CONSTANTS:lc_star TYPE c VALUE '*'.
DATA: lit_x_all TYPE STANDARD TABLE OF ztt WITH NON-UNIQUE KEY extsystem ccode ekorg werks matkl,
lit_filter_e TYPE SORTED TABLE OF ztt-extsystem WITH NON-UNIQUE KEY table_line,
lit_filter_o TYPE SORTED TABLE OF ztt-ekorg WITH NON-UNIQUE KEY table_line,
lit_filter_c TYPE SORTED TABLE OF ztt-ccode WITH NON-UNIQUE KEY table_line,
lit_filter_w TYPE SORTED TABLE OF ztt-werks WITH NON-UNIQUE KEY table_line.
SELECT *
FROM ztt
WHERE a = #i_a
INTO TABLE #lit_x_all.
LOOP AT i_pit_input INTO DATA(lwa_input).
"filter to avoid select statement in loop
lit_filter_e = VALUE #( ( CONV #( lc_star ) ) ( lwa_input-extsystem ) ).
DATA(lit_final_e) = FILTER #( lit_approver_all IN lit_filter_e WHERE extsystem = table_line ).
lit_filter_o = VALUE #( ( CONV #( lc_star ) ) ( lwa_input-ekorg ) ).
DATA(lit_final_o) = FILTER #( lit_final_e IN lit_filter_o WHERE ekorg = table_line ).
lit_filter_c = VALUE #( ( CONV #( lc_star ) ) ( lwa_input-ccode ) ).
DATA(lit_final_c) = FILTER #( lit_final_o IN lit_filter_c WHERE ccode = table_line ).
lit_filter_w = VALUE #( ( CONV #( lc_star ) ) ( lwa_input-werks ) ).
DATA(lit_final_w) = FILTER #( lit_final_c IN lit_filter_w WHERE werks = table_line ).
ENDLOOP.
Currently I am using above code with filter for each field. Can we achieve same requirement with single filter instead of multiple filters?
Thanks
Phani
The documentation states:
Table filtering can also be performed using a table comprehension or a table reduction with an iteration expression for table iterations with FOR. The operator FILTER provides a shortened format for this special case and is more efficient to execute.
As in your case filtering with FILTER does not work as you're effectively ORing the 'star case' and 'filter value case', using the VALUE constructor to perform table comprehension is the better choice:
DATA(result) = VALUE #(
FOR entry IN entries
WHERE (
( a = '*' OR a = filter-a ) AND
( b = '*' OR b = filter-b )
"...
)
( entry )
).
This should also be by magnitutes faster as it avoids the creation of multiple intermediate internal tables.

Modify an itab field using where

How can I write the below code
MODIFY git_oi_tab FROM VALUE #( dmbtr = dmbtr * -1 )
TRANSPORTING dmbtr
WHERE shkzg = 'H'.
The DMBTR in dmbtr * -1 is unknown.
Thanks
Elias
PS. I found this but is not what I want as returns the calculated field but clear all others in ITAB it_final_data
it_final_data = VALUE #( LET lit_final_data = it_final_data IN FOR lwa_final_data IN lit_final_data
( ovh_ifrs_diff_value = lwa_final_data-ovh_ifrs_value * lv_1st_ledger_amount / sum_OVH_IFRS ) ).
It appears like you want to invert the sign of the field DMBTR in all rows where the field SHKZG is equal to 'H'
In that case you won't get around doing it with a loop:
LOOP AT git_oi_tab ASSIGNING FIELD-SYMBOL(<ls_oi_tab>) WHERE shkzg = 'H'.
<ls_oi_tab>-dmbtr = <ls_oi_tab>-dmbtr * -1.
ENDLOOP.
If you use the MODIFY itab FROM wa variant, then wa is not constructed separately for each line. It is taken once as-is and applied to all lines. You can't sneak any dynamic logic in there.

How to add new line to itab with VALUE expression

ABAP 7.40 brought us new syntax, I am still figuring it out.
I want to add a new line to the existing table lt_itab. I found a workaround by adding an empty line and figuring out the current length of the table for an update by index, but is there an easier way?
SELECT spfli~carrid, carrname, connid, cityfrom, cityto
FROM scarr
INNER JOIN spfli
ON scarr~carrid = spfli~carrid
WHERE scarr~carrid = #carrier
ORDER BY scarr~carrid
INTO TABLE #DATA(lt_itab).
"How can I simplify the following code part?"
DATA(lv_idx) = lines( lt_itab ).
APPEND INITIAL LINE TO lt_itab.
lt_itab[ lv_idx + 1 ] = VALUE #( carrid = 'UA'
carrname = 'United Airlines'
connid = 941
cityfrom = 'Frankfurt'
cityto = 'San Francisco' ).
It's all in the documentation:
lt_itab = VALUE #( BASE lt_itab ( carrid = ... ) ).
The index logic is pretty ugly, you can easily use the ASSIGNING addition to the APPEND command to get a field symbol to the newly added line. You can then use that field symbol to fill the table entry using the same VALUE construct you are using now.
Or you can do it in one statement:
APPEND VALUE #( ... ) TO lt_itab.

Conversion exception while working with constructor expressions

I'm working on a routine which moves the lines of a string table (in this case fui_elements) into a structure of unknown type (fcwa_struct).
DATA(li_temp) = ... "fill assignment table here
LOOP AT fui_elements ASSIGNING FIELD-SYMBOL(<lfs_element>).
ASSIGN COMPONENT li_temp[ sy-tabix ] OF STRUCTURE fcwa_struct
TO FIELD-SYMBOL(<lfs_field>).
IF sy-subrc <> 0.
"somethings wrong with the fui_elements data
ENDIF.
<lfs_field> = <lfs_element>.
ENDLOOP.
If the table i_field_customizing (STANDARD TABLE OF string) is not initial, I want to use its values.
Otherwise I want to generate an integer table (so that the loop runs equally often regardless of the table's values). Here lw_max is the amount of fields the imported structure has:
DATA(li_temp) = COND #( WHEN i_field_customizing[] IS INITIAL
THEN VALUE t_integer_tt( FOR X = 1
THEN X + 1
WHILE X <= lw_max
( X ) )
ELSE i_field_customizing ).
But when I run the report with i_field_customizing like that:
DATA(i_field_customizing) = VALUE t_string_tt( ( `KUNNR` ) ( `NAME1` ) ).
I get this exception on the line where I try to construct li_temp:
CX_SY_CONVERSION_NO_NUMBER (KUNNR cannot be interpreted as a number)
My current guess is that COND gets its type statically. Does anybody know how I can get around this?
What you are trying to achieve will not be possible because the type of an inline definition of a variable using COND is decided at compilation time and not at runtime.
Please see my question here. The type that will be taken is always the type of the variable that stands directly after THEN. You can decide what type will be chosen at compilation time by fiddling with negating the condition and switching places of variables after THEN at ELSE but it will be always either or and from what I understand you want to be able to do it dynamically so your ASSIGN COMPONENT statement works as expected with integers.
Even by specifically casting the variable after ELSE one gets the same short dump as you do.
DATA(li_temp) = COND #( WHEN i_field_customizing IS INITIAL
THEN VALUE t_integer_tt( ( 1 ) ( 2 ) )
ELSE CAST t_string_tt( REF #( i_field_customizing ) )->* ).
Alternatively you could cast to REF TO DATA but then you have to dereference it to a field symbol of type STANDARD TABLE.
REPORT zzy.
CLASS lcl_main DEFINITION FINAL CREATE PRIVATE.
PUBLIC SECTION.
CLASS-METHODS:
main.
ENDCLASS.
CLASS lcl_main IMPLEMENTATION.
METHOD main.
TYPES:
t_integer_tt TYPE STANDARD TABLE OF i WITH EMPTY KEY,
t_string_tt TYPE STANDARD TABLE OF string WITH EMPTY KEY.
FIELD-SYMBOLS:
<fs_table> TYPE STANDARD TABLE.
DATA: BEGIN OF l_str,
kunnr TYPE kunnr,
name1 TYPE name1,
END OF l_str.
* DATA(i_field_customizing) = VALUE t_string_tt( ( `KUNNR` ) ( `NAME1` ) ).
DATA(i_field_customizing) = VALUE t_string_tt( ).
DATA(li_temp) = COND #( WHEN i_field_customizing IS INITIAL
THEN CAST data( NEW t_integer_tt( ( 1 ) ( 2 ) ) )
ELSE CAST data( REF #( i_field_customizing ) ) ).
ASSIGN li_temp->* TO <fs_table>.
LOOP AT <fs_table> ASSIGNING FIELD-SYMBOL(<fs_temp>).
ASSIGN COMPONENT <fs_temp> OF STRUCTURE l_str TO FIELD-SYMBOL(<fs_field>).
ENDLOOP.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
lcl_main=>main( ).