I’d like to use a dynamic array in my code and found some documentation about X-Arrays. My version of Natural is 4.2.6, so I believe this should work. When I run my program, it doesn’t blow up. However, it doesn’t show me any output for my dynamic array. Here’s my code (with file and key names changed to generic names):
In my local data area, I have my array defined as:
READ WORK FILE 4 #WKF04
ADD 1 TO #WF4-READ
MOVE #X TO #KEY-X
MOVE #ITEM TO #KEY-ITEM
FD-IT. FIND [FILE] WITH [KEY] = #KEY
IF *NUMBER(FD-IT.) = 0
ADD 1 TO #NOFOUND
DISPLAY '=' #NOFOUND /* JUST TO SEE IF MY CODE GETS THIS FAR
EXPAND ARRAY #ITEM-ARRAY TO (1:#NOFOUND) /*EXPAND X-ARRAY
MOVE #ITEM TO #ITEM-ARRAY(#NOFOUND)
ESCAPE BOTTOM
END-IF
ADD 1 TO #FOUND
.... continue processing for found records
END-FIND
END-WORK
*
WRITE 'RESULTS OF PROGRAM:' /
#WF4-READ (EM=ZZZ,ZZZ,ZZZ,ZZ9) 'WORK FILE 4 RECS READ' /
#FOUND(EM=ZZZ,ZZZ,ZZZ,ZZ9) 'RECS FOUND'
IF #NOFOUND > 0
WRITE #NOFOUND(EM=ZZZ,ZZZ,ZZZ,ZZ9) 'RECS NOT FOUND'
FOR #I = 1 TO #NOFOUND
WRITE / 21T #ITEM-ARRAY(#I)
END-FOR
END-IF
END
Here is my output:
PAGE 1 #NOFOUND
RESULTS OF PROGRAM:
134 WORK FILE 4 RECS READ
0 RECS FOUND
I’ve manipulated my work file data so that all 134 records should not be found. Any ideas of where I may be going wrong?
If your FIND at line 5 does not find anything, it will not enter the loop, so the IF *NUMBER at line 6 never gets executed. You need to move the IF *NUMBER below the find loop.
Thanks for your response, Steve. I was told to steer clear of the IF NO RECORDS FOUND clause because it initiates another processing loop whereas the IF *NUMBER = 0 avoids that. Is that not true or is the difference between the two negligible?
Thank you, Wolfgang. This shows me that I need to do my homework and read the Software AG documentation to verify any future “helpful programming tips” that I read out there online.
Just a few other notes: the EXPAND (or RESIZE) command is a relatively expensive one to use - typical expansion requires allocating new space, copying old values in and freeing up old space.
Whenever you know what size your array will be, use that rather than incrementing the array size one element at a time. In some cases, *NUMBER contains the number of records that will be returned, so you can use an AT START OF DATA or IF *COUNTER = 1 and EXPAND the array to hold *NUMBER occurrences. If your loop will contain conditional logic such that your array will likely end up with fewer than *NUMBER occurrences filled in, use the REDUCE statement outside the loop to reduce the dynamic array down to its actual occurrences. In your example, you might start with an estimate of how many items will be read and match your criterion for adding to the array, and if you fill that up, increase the array by some factor (25%? 50%? 100 occurrences? 1000 occurrences? know your data!) with an EXPAND before adding the next row of data rather than doing an EXPAND on every iteration through the loop.
Other application scenarios may have other logic needed, but the goal is to minimize the number of times you have to execute the EXPAND statement to help minimize CPU usage.
Thanks for the good suggestion, Douglas. If I understand you correctly, I could initialize my upper bound of the array at 100, and then if that upper bound is reached, increase it by a certain percentage or number. So my code would then look like this:
READ WORK FILE 4 #WKF04
ADD 1 TO #WF4-READ
MOVE #X TO #KEY-X
MOVE #ITEM TO #KEY-ITEM
FD-IT. FIND [FILE] WITH [KEY] = #KEY
IF NO RECORDS FOUND
ADD 1 TO #NOFOUND
IF #NOFOUND = 1
EXPAND ARRAY #ITEM-ARRAY TO (1:#U-BOUND) /*INITIAL EXPANSION OF X-ARRAY (INITIALIZED AT 100 IN LDA)
END-IF
MOVE #ITEM TO #ITEM-ARRAY(#NOFOUND)
IF #NOFOUND = #U-BOUND
COMPUTE #U-BOUND = #U-BOUND + 25
EXPAND ARRAY #ITEM-ARRAY TO (1:#U-BOUND) /*INCREASE UPPER BOUND OF X-ARRAY
END-IF
ESCAPE BOTTOM
END-NOREC
ADD 1 TO #FOUND
.... continue processing for found records
END-FIND
END-WORK