0% found this document useful (0 votes)
4 views36 pages

Rpgarraysfortoday Part2

Use for Arrays in RPG AS400 Programas Part 2

Uploaded by

Willian Serpas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views36 pages

Rpgarraysfortoday Part2

Use for Arrays in RPG AS400 Programas Part 2

Uploaded by

Willian Serpas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 36

RPG Arrays for Today- Part 2

Speaker: Jon Paris

Modernizing IBM i?
Let's Take Another Look at Data Access
Speaker: Bill Langston

Sponsored by:

Download handout for Jon's RPG Arrays topic:

Note: Link is case sensitive


cutt.ly/RPGArraysPart2

See more Summit Lunch & Learn webinars at


SystemiDeveloper.com/LunchLearn
Notes
About Me:
I am the co-founder of Partner400, a company specializing in customized education and mentoring services for IBM i
(AS/400, System i, iSeries, etc.) developers. My career in IT spans 50+ years including a 12 year period with IBM's
Toronto Laboratory.

Together with my partner Susan Gantner, I devote my time to educating developers on techniques and technologies
to extend and modernize their applications and development environments. Together Susan and have authored
many technical articles for a number of publications, including IT Jungle's RPG Guru column (www.itjungle.com).

The easiest way to find our articles is to use our Authory web site authory.com/JonParisAndSusanGantner/

In addition to my work with Partner400 I am also a founding member of the System i Developer (SiD) education
consortium - originators of the highly successful RPG & Db2 Summit. More on SiD's offerings at
SystemiDeveloper.com

Feel free to contact me any time: Jon.Paris @ partner400.com

© Copyright Partner400, 2023. Page 2


RPG Arrays for Today - Part 2
We'll Start with some new array-oriented BIFs
• %MAXARR and %MINARR
• %SPLIT
• %CONCATARR
• %LIST

Then move on to dynamic arrays


• There's more than one kind!
• They work really well with SQL

© Copyright Partner400, 2023. Page 3


%MAXARR and %MINARR V7.3+
Max and Min support for arrays
The BIFs return the relevant array index
• %MaxArr returns the index of the highest element
• and %MinArr the index of the lowest

dcl-s salesByMonth packed(7:2) Dim(12);


dcl-s maxValue packed(9:2);
dcl-s minValue packed(9:2);

salesByMonth = %list( 12: 20: 99: 0: 125: 33: 27: 288: 350: 1234:
765: 552 );
maxValue = %maxarr(salesByMonth);
minValue = %minArr(salesByMonth);
Dsply ( 'High index ' + %char(%maxarr(salesByMonth)) +
' - value: ' + %char(salesByMonth(%maxarr(salesByMonth))));
Dsply ( 'Low index ' + %char(%minarr(salesByMonth)) +
' - value: ' + %char(salesByMonth(%minarr(salesByMonth))));

© Copyright Partner400, 2023. Page 4


Notes
When the %MIN and %MAX BIFs were introduced I was happy to see them, but I thought at the time that it would
also be nice to have a variant that operated on arrays rather than a list of values.
The problem was that those BIFs return the actual value and that is not terribly useful when dealing with an array -
you really want the index of the value so that you can access any related data in (say) an array DS.

%MINARR and %MAXARR fill that gap. %MINARR returns the index of the lowest value in the array and
%MAXARR returns the index of the highest. These BIFs can be applied to a conventional array or to a DS array.

This latter capability is particularly useful as we will see shortly

© Copyright Partner400, 2023. Page 5


%MAXARR and %MINARR (contd.)
Using the returned index to access associated data

// Assume this array was loaded with sales data via


// SQL or native I/O or ...
Dcl-Ds salesData qualified Dim(200);
salesRep# char(5);
name varchar(30);
salesToDate packed(9:2);
end-ds;
dcl-s index int(5);

// Identify the poorest performer


index = %minArr( salesData(*).salesToDate );

Dsply ( 'Poorest performing rep is currently ...' );


Dsply ( 'Rep #' + salesData(index).salesRep#
+ ' Name: ' + salesData(index).name );

DSPLY Poorest performing rep is currently ...


DSPLY Rep #2345 Name: Unlucky Smith
© Copyright Partner400, 2023. Page 6
Notes
As you can see the ability to retrieve the index of the lowest (or highest) value in the array makes it simple to access
associated information.

The example is a little unrealistic in that it assumes all 200 array entries are filled. In practice, I would expect to have
a count of the number of active elements and use that together with %SubArr to restrict the scope of the BIF to the
number of active elements. e.g something like:

index = %minArr( %SubArr( salesData(*).salesToDate : 1: activeCount );

As you will see in part 2 - an even better approach would be to use a dynamically sized array which would avoid the
need for the %SubArr and the active element count.

You could, of course, omit the intermediate step of storing the index if you only wanted one item. For example to
retrieve just the name you could do this:

lowPerformer = salesData( %minArr( salesData(*).salesToDate ) ).name );

But I don't think it makes for the most maintainable of code.

© Copyright Partner400, 2023. Page 7


%SPLIT V7.3+
Forms an array from the content of a string
%SPLIT ( string { : separator(s) } )
Default separator is a space
• Or you can specify your own with the optional separators parameter
✦ separators can be a single character or multiple characters - see next chart

dcl-s textString varchar(50)


inz('The quick brown fox jumps over the lazy dog');
dcl-s words varchar(20) dim(20);
dcl-s word like(words);

words = %Split( textString ); // Fill array from textString

for-each word in words;


Dsply word;
EndFor;
// Alternative use if the array does not need to be retained
for-each word in %Split( textString );
Dsply word;
EndFor;
© Copyright Partner400, 2023. Page 8
Notes
The BIF “splits” the input into substrings using the default separator of a space. As a result, element 1 of the
array words now contains “The”, element 2 “quick”, and so on.

If there are multiple consecutive spaces they will be ignored so there will be no "empty" array elements returned.

On the next chart we will look at specifying alternative separators.

© Copyright Partner400, 2023. Page 9


%SPLIT (contd.)
This is an example of using multiple separators
• Each character is considered separately
• Consecutive separators are ignored
✦ See the notes page for the output to verify this
%Split can be problematic with (say) CSVs
• In such cases you probably don't want to ignore consecutive separators
✦ e.g. "ABC",123,,"DEF" represents four values but %Split only gives you three
• In these circumstances some pre-processing of the input may be required.

dcl-s paragraph varchar(120)


inz('This short paragraph contains commas, "quotes", and +
periods. Is there a question? Better believe it!');
dcl-s item varchar(20);
// This demonstrates the use of multiple separators
// i.e. space, comma, period, quotes, question mark and exclamation.
for-each item in %Split( paragraph : ' ,."!?' );
Dsply item;
EndFor;
© Copyright Partner400, 2023. Page 10
Notes
The example shows how you could break up a sentence such that most punctuation marks are ignored. Notice that I included a
blank in the string so that spaces would count as a separator. If you also wanted to ignore (say) single quotation marks etc. you
would simply add them to the separator list.
Here's the output from this piece of code
DSPLY This
DSPLY short
DSPLY paragraph
DSPLY contains
DSPLY commas
DSPLY quotes
DSPLY and
DSPLY periods
DSPLY Is
DSPLY there
DSPLY a
DSPLY question
DSPLY Better
DSPLY believe
DSPLY it

A warning though. %SPLIT ignores consecutive separators. Most of the time that is a good thing but can present a problem in
some circumstances. For example if you were processing a CSV file containing the string "ABC",123,,"DEF" using CPYFRMIMPF
it would result in four fields. “ABC”, 123, blank, and “DEF”. However, %SPLIT would only produce three array elements because
the second comma would be ignored. So, in these circumstances some pre-processing of the input may be required.

© Copyright Partner400, 2023. Page 11


%CONCATARR V7.4+
Concatenates ALL elements in a character array using a common separator
• Effectively the opposite of %SPLIT
To restrict the number of elements processed ...
• Use %SUBARR to define the active elements
• Or use a dynamic array so that only active elements are processed
✦ More about these later

dcl-s names varchar(20) dim(3);


// names(1) = '/Home'
// names(2) = 'JonP'
// names(3) = 'NewFile.txt'

fullPath = %CONCATARR('/' : names );


// fullPath now = "/Home/JonP/NewFile.txt"

// If we only wanted the path portion then ...


path = %CONCATARR('/' : %SubArr( names : 1 : 2 ));
// path now = "/Home/JonP"
© Copyright Partner400, 2023. Page 12
Define a Temporary Array - %LIST BIF V7.3+
%LIST is allowed anywhere where an array is allowed
• Except SORTA, %LOOKUPxx, and %SUBARR
✦ i.e. situations where it would not make sense to use it
Values in the list can be variables, function calls, literals, etc.
• But must all be of a compatible data type
✦ e.g. All char, or all numeric, or all date
It can be used to assign values to an array
• For example to assigning a series of fields to an array

Dcl-S users char(10) dim(5);

Dcl-S monthlySales packed(7:2) Dim(12);

// Load the users array


users = %list ('QSECOFR' : 'QUSR': currentUser());

// Load monthly sales figures into array


monthlySales = %List( janSales : febSales : ... : decSales );

© Copyright Partner400, 2023. Page 13


Notes
%List represents a temporary array. It is a list of values which will are assigned as consecutive elements.
In the second example, %List is being used as a simple approach for copying non contiguous database columns to
an array. There are other ways of doing this including, of course, copying the fields one by one. But this is by far the
cleanest and I think most intuitive way of doing it.
As you will see later %Lists can also be processed directly as an array or used to assign values to an array.

© Copyright Partner400, 2023. Page 14


More Uses for %LIST
%LIST can be used with the IN operator to replace a lengthy IF list
• Far "cleaner" and easier to understand
It can also be used with FOR-EACH
• E.g. Using a fixed list of options as per the first example
• Or using data pulled from a file

// Instead of this:
If ( orderStatus = OVERDUE ) or
( orderStatus = PENDING ) or
( orderStatus = CANCELLED ) or
( orderStatus = COMPLETE );

// We can simply code this:


If orderStatus IN %LIST( OVERDUE: PENDING: CANCELLED: COMPLETE );

FOR-EACH status in %LIST( OVERDUE: PENDING: CANCELLED: COMPLETE );


// ... work with each 'status' in turn
ENDFOR;

© Copyright Partner400, 2023. Page 15


RPG Arrays for Today- Part 2
Speaker: Jon Paris

Modernizing IBM i?
Let's Take Another Look at Data Access
Speaker: Bill Langston

Sponsored by:

Download handout for Jon's RPG Arrays topic:

Note: Link is case sensitive


cutt.ly/RPGArraysPart2

See more Summit Lunch & Learn webinars at


SystemiDeveloper.com/LunchLearn
© Copyright Partner400, 2023. Page 16
Variable Dimension Arrays V7.4+
Something we have needed for a long, long time
• There are still a few "gaps" in the support but most won't notice them

Two "flavors" - Variable and dynamic


• Variable
✦ Programmer sets the current maximum length as required
• Dynamic Length
✦ RPG sets the maximum as needed
- Fabulous for SQL result sets!

A maximum is specified to provide a sanity check


• An error is generated if the maximum is exceeded

There is actually a third "flavor" - *CTDATA


• For compile time data arrays (but I hate CTDATA!)
© Copyright Partner400, 2023. Page 17
Notes
Maybe it is just me, but whenever I set up an array I always find myself conflicted as to whether to make the array as
big as it could ever conceivably need to be or even bigger. But of course if I do that I am "wasting" memory. Of
course no matter what choice I make, one day it is just not going to be big enough and the program will blow up!

Those days are now behind me - I can now make the array potentially massive without using up more memory than I
actually need.

An added benefit is that RPG keeps track of the current capacity of the array and so I can use any array operation
(such as IN, SORTA or %LOOKUP) more easily. No need to concern myself with using %SUBARR to restrict the
operation to active elements. That alone is good enough reason to use this support.

© Copyright Partner400, 2023. Page 18


Defining Variable Dimension Arrays
Variable Length
• Specified as DIM ( *VAR : maxentries )
✦ e.g. DIM ( *VAR : 500 );
• Length can be changed by the programmer when required
Dynamic Length
• DIM ( *AUTO : maxentries )
✦ e.g DIM ( *AUTO : 1000 )
• Length is changed automatically by RPG as required
Compile Time Data
• DIM ( *CTDATA : maxentries )
✦ e.g DIM ( *CTDATA : 30 )
• Length is determined at compile time

maxentries specifies the absolute maximum capacity for the array


• Basically provides a sanity check to alert you to runaway code
© Copyright Partner400, 2023. Page 19
Notes
If I even find myself forced to maintain a program that uses CTDTA I will certainly switch to this option as it frees me
to simply add/remove entries without having to count and modify the DIM(nn) entry.

But I still prefer to never use them.

© Copyright Partner400, 2023. Page 20


Variable Dimension Arrays ... more
DIM ( *VAR : maxentries )
• Starts off with a current capacity of Zero
• You can increase or decrease the capacity by using %ELEM
DIM ( *AUTO : maxentries )
• Starts off with a current capacity of Zero
• Automagically increases the capacity to accommodate the highest
index used to-date
SORTA is constrained by the current capacity
• Not by the maximum array size as with traditional RPG arrays
• Provides a performance boost without the added complexity of
using %SUBARR
%LOOKUPxx operations are similarly controlled

© Copyright Partner400, 2023. Page 21


Notes
Most modern programming languages do not force you to pre-declare the size of an array. Until now RPG has been
somewhat of an outlier in that regard. But no more!

IBM have added this feature while still maintaining the existing array support. So there's nothing new to learn, just
some nice additional capabilities to the things we are already familiar with.

© Copyright Partner400, 2023. Page 22


Getting/Setting Element Limits
%ELEM ( ArrayName : Option )
Available Options are:
• not specified
✦ On the Right Hand Side (RHS) returns the current number of active elements
- e.g. ActiveCount = %Elem(MyDynamicArray);
✦ On the Left Hand Side (LHS) sets the number of active elements
- e.g. %Elem(MyDynamicArray) = 50;
• *KEEP
✦ When increasing maximum elements preserve any existing values
- The default is to initialize new elements to the default value
• *ALLOC
✦ On the RHS returns the number of elements for which storage is allocated
✦ On the LHS requests allocation of storage for the specified number of elements
• *MAX
✦ Returns the maximum number of elements

See the Notes page for links to details of the usage of *KEEP
© Copyright Partner400, 2023. Page 23
Notes
The idea that %ELEM can be used on the left or right hand side of an expression takes a bit of getting used to.
For example if we wanted to increase the maximum number of array elements by 50% we could do this:

%Elem ( MyArray ) += %Int ( %Elem ( MyArray ) * 0.5 );

I have written a few articles on the use of dynamic arrays for ITJungle.

https://www.itjungle.com/2020/07/20/guru-dynamic-arrays-come-to-rpg/
https://www.itjungle.com/2020/08/17/guru-dynamic-arrays-come-to-rpg-the-next-part-of-the-story/
https://www.itjungle.com/2020/10/12/guru-dynamic-arrays-come-to-rpg-limitations-circumventions-and-more/

If you are interested in using dynamic arrays as parameters then the last of those is the one you need to study as it
is an area that I do not cover in the session.

© Copyright Partner400, 2023. Page 24


%ELEM() - Changing The Array Size
%ELEM can be used to increase or reduce current array size
• Added elements are initialized to the default value
✦ Unless *KEEP is used in which case no initialization takes place
• If the array is reduced in size the memory is not cleared
✦ The existing values will remain but be inaccessible via array operations
- Unless and until the size is increased again with the *KEEP option

Dcl-S VaryArray3 Int(5) Dim(*Var: 100);


Dcl-S i Int(5);
%Elem(VaryArray3) = 10; // Set to capacity of 10
For i = 1 to %Elem(VaryArray3);
VaryArray3(i) = i;
EndFor;
Dsply ('Element 5 has the value ' + %Char(VaryArray3(5)) );
%Elem(VaryArray3) = 1; // Set to 1 element
%Elem(VaryArray3 : *KEEP ) = 5; // And now to 5
Dsply ('Element 5 should still be 5 and is ' +
%Char(VaryArray3(5)) );
© Copyright Partner400, 2023. Page 25
Notes
When an array is defined as *VAR (Variable) in size, %Elem must be used to control the current maximum size. The
maximum starts at zero so %Elem must be used before any elements are stored in the array.
If we start the program by coding:
MyArray(1) = 'New Value';
and have not set the %Elem value to 1 (or greater) the attempt to store the value will fail in the same way as the old
RPG fails if you try to store a value in the 11th element of a 10 element array. Similarly
MyArray(11) = 'New Value';
will fail if the current limit is set to 10.

Note that %ELEM cannot increase the number of elements for an array beyond the maximum. So if the array is
defined as:
Dcl-S MYArray Char(12) Dim( *Var : 1000 );
Then
%ELem(MyArray : 1001);
Will also fail because it attempts to increase the current size of the array beyond its declared maximum.

© Copyright Partner400, 2023. Page 26


Special Indexing Feature - *Next
Dynamic Arrays defined with *AUTO offer an additional feature
• No need to keep track of the index when adding new elements
✦ Simply use *NEXT as the index
• You can retrieve the active maximum at any time with %ELEM

Dcl-S VaryArray Int(5) Dim(*Auto:9999);

Dsply ('Start: Current Capacity = ' + %Char(%Elem(VaryArray)) );


// Should display a value of zero

For i = 1 to 50;
VaryArray(*Next) = i;
EndFor;

Dsply ('End: Current Capacity = ' + %Char(%Elem(VaryArray)) );


// Should display a value of 50

Dsply ('Array cannot exceed: '


+ %Char(%Elem(VaryArray : *Max)) + ' elements');
// Should display a value of 9999
© Copyright Partner400, 2023. Page 27
Notes
I really like this feature - simply use *NEXT as the index and the compiler takes care of the index number and
updates the active element count. If you ever actually need the count, simply use %Elem(arrayName). Like all BIFs,
it can be used anywhere that a variable of that type can be used. So you can return it from a subprocedure, or
assign if to a variable, or use it as a parameter, or ...
*NEXT is effectively what happens when you use a dynamic array to receive the results of an SQL query. Plus point
is that you can retrieve the two count by simply looking at the current value of %Elem. Don't forget to set %Elem to
zero though prior to retrieving the SQL results.

© Copyright Partner400, 2023. Page 28


Using SQL With Dynamic Arrays
SQL works really well with the *AUTO option
• Just load the array and iterate through it with FOR-EACH
• %Elem will give you the active count if you need it

Dcl-Ds results ExtName('ANIMALS') qualified Dim( *Auto : 1000 );


End-Ds;

Dcl-Ds animal likeDs(results);

Dcl-s type like(results.type);

Exec SQL
Declare animalsCursor cursor for
Select * from ANIMALS where type = :type;

Exec SQL
Open animalsCursor;

Exec SQL
Fetch from animalsCursor for 1000 rows into :results;

© Copyright Partner400, 2023. Page 29


Using SQL With Dynamic Arrays - Contd.
There is no need to set the element count back to zero each time
• SQL seems to do that under the covers
We used %ELEM to give the row count
• No need to use SQL Diagnostics or SQLErrD(3)

You can also use a *VAR type of dynamic array but ...
• I found I had to use an "Insensitive Scroll" cursor in order to be able to obtain
the row count prior to the fetch
• Then use that count to set the number of elements

Dsply ( 'Found ' + %Char( %Elem( results ) ) + ' matching animals' );

For-Each animal in results;


Dsply ( 'Id: ' + %Char(animal.Id) + ' - Name: ' + animal.name );
EndFor;

Exec SQL
Close animalsCursor;

© Copyright Partner400, 2023. Page 30


Danger Will Robinson ... Caution Needed ...
If the current size of the array changes it may move in memory
• If you have used %Addr to obtain the array's address you must
refresh that address after each change in size

Debugger is blissfully unaware that the array size may change


• So you must avoid trying to access elements beyond the current
size
• The special variable _QRNU_VARDIM_ELEMS_arrayname
contains the current maximum and can be queried
✦ But changing its value will have zero effect

© Copyright Partner400, 2023. Page 31


Notes
It is important to understand that, unlike conventional RPG arrays, dynamic arrays are just that - dynamic! And that
means that as their size changes they may move around in memory. For this reason it is critical that if you capture
the address of the array for any reason, you must use %Addr() to refresh that address prior to attempting to actually
use the pointer.

© Copyright Partner400, 2023. Page 32


Limitations of New Array Support
Can only be used on a top-level definition
• i.e. a stand-alone array or a DS array
✦ Not yet supported for nested arrays

Cannot be ...
• Used as a parameter unless the prototype specifies
Options(*VarSize)
• Used for procedure return values
• Used in prototype parameter definitions
• Used on fixed-form calculations (i.e. the old C-specs)
• And a number of other more esoteric restrictions ...
✦ See the Notes page for the full list

© Copyright Partner400, 2023. Page 33


Notes
The manual lists the following as the full set of restrictions. As you'll see, many are things that you probably would
never attempt to do anyway but ...

A varying-dimension array can only be a top-level definition, either a standalone array or a data structure array.
Tables, multiple-occurrence data structures, subfields, procedure return values, and procedure parameters are not
allowed.
A varying-dimension array cannot be used in fixed-form calculations.
A varying-dimension array cannot be based on a pointer.
A varying-dimension array cannot be imported or exported.
When a varying-dimension array is passed as a parameter to a prototyped procedure or program, the parameter
must be defined with OPTIONS(*VARSIZE).
A varying-dimension array cannot have type Object.
A varying-dimension array cannot be null-capable.
A subfield of a varying-dimension data structure array cannot be null-capable unless the null-indicator is also a
subfield in the same data structure.
A varying-dimension array cannot appear on an Input specification unless an index is specified.
A varying-dimension array cannot appear on an Output specification unless an index is specified.
A varying-dimension array element cannot appear on a Lookahead Input specification.
A varying-dimension array cannot be a compile-time array or a pre-run array. The CTDATA and FROMFILE
keywords cannot be used.

© Copyright Partner400, 2023. Page 34


Any Questions ?

That's All
Folks
More questions? e-mail me at:

[email protected]

© Copyright Partner400, 2023. Page 35


RPG Arrays for Today- Part 2
Speaker: Jon Paris

Modernizing IBM i?
Let's Take Another Look at Data Access
Speaker: Bill Langston

Sponsored by:

Download handout for Jon's RPG Arrays topic:

Note: Link is case sensitive


cutt.ly/RPGArraysPart2

See more Summit Lunch & Learn webinars at


SystemiDeveloper.com/LunchLearn
© Copyright Partner400, 2023. Page 36

You might also like