The latest Technology Refreshes, IBM i 7.4 TR3 and 7.3 TR9, have added something I have wanted for a long time added to the RPG programming language, the ability to test if a value is within a range. This has been achieved by the introduction of a new Built in Function, %RANGE, and a new operation code, IN.
The syntax is very similar to the Range test I use in SQL:
if <value or variable> in %range(<minimum value or variable> : <maximum value or variable>) ; |
Let me get on with some simple examples. First with a date:
01 **free 02 dcl-s InRange ind ; 06 if %date() in %range(d'2020-11-01' : d'2020-11-30') ; 07 InRange = *on ; 08 endif ; 09 dsply ('Date = ' + %char(InRange)) ; |
Line 1: If I am doing things in RPG why would I want to do it in an old version of our favorite programming language?
Line 2: The only purpose for this variable is to know if the Range test was successful or not.
Lines 3 – 5: Will be shown later.
Line 6: Testing the current date, using built in function %DATE() with no parameter, if it is in this month, November 1 – 30, and this year.
Line 7: If the test was successful, today is in this month, then the indicator InRange is turned on.
Line 9: I am using the Display operation code, DSPLY, to see the value of the indicator and whether my test was successful. Which it was:
DSPLY Date = 1 |
Now with numbers...
10 if 7 in %range(1 : 10) ; 11 InRange = *on ; 12 else ; 13 InRange = *off ; 14 endif ; 15 dsply ('Number = ' + %char(InRange)) ; |
Line 10: Is the number seven in the range of numbers between one and ten?
Line 11: Of course it is.
Line 15: So my indicator will display:
DSPLY Number = 1 |
Next character values...
16 if 'ABCD' in %range('A' : 'B') ; 17 InRange = *on ; 18 else ; 19 InRange = *off ; 20 endif ; 21 dsply ('Char 1 = ' + %char(InRange)) ; |
Line 16: "ABCD" is between the values of "A" and "B".
Line 21: Therefore, the indicator is on.
DSPLY Char 1 = 1 |
What happens if I reverse the values in the Range BiF, and put the higher value in the first parameter and the lower in the second.
22 if 'ABCD' in %range('B' : 'A') ; 23 InRange = *on ; 24 else ; 25 InRange = *off ; 26 endif ; 27 dsply ('Char 2 = ' + %char(InRange)) ; |
Line 22: No value can satisfy this range criteria.
Line 27: And the indicator is off.
DSPLY Char 2 = 0 |
I have used values above, not variables. Here are a couple examples using variables:
03 dcl-s MinValue char(10) inz('Houston') ; 04 dcl-s MaxValue like(MinValue) inz('Yznaga') ; 05 dcl-s TestValue like(MinValue) inz('Dallas') ; 28 if TestValue in %range(MinValue : MaxValue) ; 29 InRange = *on ; 30 else ; 31 InRange = *off ; 32 endif ; 33 dsply ('City 1 = ' + %char(InRange)) ; |
Lines 3 – 5: Here are those missing lines I mentioned before, that define the three variables I will be using.
Line 28: Testing if the city Dallas is within the range of city names of Houston to Yznaga.
Line 33: And it is not so the indicator is off.
DSPLY City 1 = 0 |
Let me try that again with another city name.
34 TestValue = 'Round Rock' ; 35 if TestValue in %range(MinValue : MaxValue) ; 36 InRange = *on ; 37 else ; 38 InRange = *off ; 39 endif ; 40 dsply ('City 2 = ' + %char(InRange)) ; |
Line 35: The city of Round Rock is in the range.
Line 40: And the indicator shows that.
DSPLY City 2 = 1 |
These two are a great addition and I can see me using them a lot. Now what about a %VALUES BiF?
You can learn more about the %RANGE Built in Function from the IBM website here.
This article was written for IBM i 7.4 TR3 and 7.3 TR9.
Happy to hear about this. Thanks. Also, can tell you're a Texan now!
ReplyDeleteif 'ABCD' in %range('A' : 'B') ;
ReplyDeleteHow is this true? Is only the first byte being tested, because the range values are 1 byte?
All code I show is tested before I include it in a post. I have to admit I could not find a reference in the documentation stating if it only checks the first byte or whether is checks the entire string.
DeleteDocumentation says those statements are equivalent
DeleteIF x IN %RANGE(y1 : y2);
IF x >= y1 AND x <= y2;
I think
if 'ABCD' in %range('A' : 'B') ; is equivalent to
if 'ABCD' >= 'A' and 'ABCD' <= 'B'
and what you have in mind is
if %subst('ABCD':1:1) in %range('A' : 'B') ;
If you compare 'ABCD' with 'A', 'ABCD' is greater then 'A' but if you compare 'ABCD' with 'B, 'ABCD' is lower then 'A' so the result of %range is correct.
ReplyDeleteIs like you made
IF 'ABCD' IN %RANGE('A ' : 'B ');
Try this
ReplyDeleteIF 'ABCD' IN %RANGE('A' : 'A'); This is false so you can understand that is not the first byte tested
can you use it to set an indicator field directly:
ReplyDeleteTestFldValidInd = testFld in %range(minFld : maxFld);
I am thinking for screen validation routine, flagging all of the errors and redisplaying
ReplyDeleteDo you prefer the "if" wrapper or write the check in one line like:
ReplyDeleteInRange = ( TestValue in %range(MinValue : MaxValue)) ;
if the only logic I am going to do is set an indicator based on the results of the if, I prefer to set it directly with comments as to what it is doing.
DeleteSimon, what do you mean by a %VALUES bif?
ReplyDeleteIt is a long held wish of mine where I could have a BiF that I could then compare a variable to a list of values.
DeleteBut as you know that is now possible and will be revealed tomorrow.
You already have it Simon - it os called %LIST and is in the same set of PTFs.
DeleteI was using this as a "teaser" for today's post, which gives plenty examples of using %LIST.
DeleteIf Answer in %Values(‘Y’: ‘N’); would be nice, where the values can be any number of parameters.
ReplyDeleteperfecto! saludos.
ReplyDeleteSimon, thanks for sharing. Great read with great example. It’s been a long time coming. The %range BIF (build-in- function) will be very useful with the new types of data being added to the box. Thanks.
ReplyDeleteY cómo se hace la validación contraria?, por ejemplo if not (x> 0 and x<5)
ReplyDeleteIN %RANGE returns an indicator value. If I wanted to check if something is not within the range I could do the following:
Deletedcl-s InRange ind ;
InRange = DTEBIRTH in %range(d'1910-01-01' : d'1920-01-01') ;
if not(InRange) ;
dsply ('Not in range ' + %char(DTEBIRTH)) ;
endif ;
InRange will be "off" if the value is not in the range.