is it possible to use MaximumFuntion within an Entry Funciton in Progress 4gl - progress-4gl

I am new to progress and I am trying to figure out how to get this working. My task is to Get a list of integer values from user as semi colon separated and message the highest and lowest value on that list. Till now I have used an entry function to help me get just the integers entered by the user one after another. like so
repeat I = 1 to totalEntries:
m = entry (I, Userinput, ";").
display m.
end.
After this I would like to find out the maximum value of all the entries. how can I do this since maximum function accepts more than one value for comparison.

There is no built in function to give a maximum or minimum number from given list of numbers. You need to write your own logic as in most of the programming languages. Here is an example:
DEF VAR i AS INT.
DEF VAR nlist AS CHAR INIT "1;2;7;3;6;9".
DEF VAR imin AS INT.
DEF VAR imax AS INT.
imin = INTEGER(ENTRY (1, nlist, ";")).
imax = INTEGER(ENTRY (1, nlist, ";")).
REPEAT i = 2 TO NUM-ENTRIES(nlist, ";"):
IF INTEGER(ENTRY(i, nlist, ";")) > imax THEN
imax = INTEGER(ENTRY(i, nlist, ";")).
IF INTEGER(ENTRY(i, nlist, ";")) < imin THEN
imin = INTEGER(ENTRY(i, nlist, ";")).
END.
MESSAGE imax.
MESSAGE imin.

As Austin sad, there is no built-in function in Progress to give a maximum or minimum number from a list.
In your comment, you've mentioned that MAXIMUM(1,2,3) worked. Yes, it works, but you have to figure that you're passing three parameters to the function, not a list of numbers inside a single CHAR variable.
To solve your problem you can use the solution given by Austin or you can use two functions that receive a CHAR variable with semi colon separated values and return maximum or minimum values.
Here is an example, based on your code.
FUNCTION iMax RETURNS INTEGER
( INPUT pData AS CHAR ):
DEF VAR iOutput AS INT NO-UNDO.
DEF VAR iCount AS INT NO-UNDO.
iOutput = ?.
DO iCount = 1 TO NUM-ENTRIES(pData,';'):
IF iOutput = ? THEN DO:
iOutput = INT(ENTRY(iCount,pData,';')).
NEXT.
END.
iOutput = MAX(iOutput,INT(ENTRY(iCount,pData,';'))).
END.
RETURN iOutput.
END FUNCTION.
FUNCTION iMin RETURNS INTEGER
( INPUT pData AS CHAR ):
DEF VAR iOutput AS INT NO-UNDO.
DEF VAR iCount AS INT NO-UNDO.
iOutput = ?.
DO iCount = 1 TO NUM-ENTRIES(pData,';'):
IF iOutput = ? THEN DO:
iOutput = INT(ENTRY(iCount,pData,';')).
NEXT.
END.
iOutput = MIN(iOutput,INT(ENTRY(iCount,pData,';'))).
END.
RETURN iOutput.
END FUNCTION.
/****************/
Define variable NumberEntry as character view-as fill-in no-undo.
Define variable UsersInput as character no-undo.
Define variable i as integer no-undo.
Define variable totalEntries as integer no-undo.
Define variable m as character no-undo.
Define variable n as character no-undo.
Define button bFind.
Define frame main numberEntry label "Enter numbers separated by semi colon" skip
bFind label "Find Max and Min" with side-labels. /*Trigger for button*/
On choose of bFind in frame main do: /*Retrieve the users input*/
Usersinput = (numberEntry:screen-value). /*to find out how many characters the user has enterd.*/ totalEntries = num-entries(UsersInput,';'). Display totalentries. /*Logic to extract Users input values one by one.*/
Repeat i = 1 to totalEntries: M = entry(i, UsersInput, ";").
Display m.
End. /*Logic to find the maximum element. */ .....
MESSAGE 'MAXIMUM :' iMax(UsersInput) SKIP
'MINIMUM :' iMin(UsersInput)
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
VIEW FRAME main.
ENABLE ALL WITH FRAME main.
WAIT-FOR CHOOSE OF bfind.
You can call iMax() or iMin() and get MAX or MIN values from Progress MAXIMUM and MINIMUM function using a CHAR list of INTEGER values separated by semi colons without need to make a full code block to do the comparision and get the information for each situation that presents necessary.
Hope it helps.

Related

Progress 4GL - update

how can I check if update was successful when I run this For Each
FOR EACH products
WHERE products.name = "ProductsName":
update price = 1000.
END.
Sometimes this For Each is ok, but sometimes when record is lock it doesn't work. I need run this For Each via WebSpeed and return true when For Each is successful or false when not. How can I get this result?
You should add more details to your request, but try this it might help get you started:
procedure update_items:
define output parameter records_read as integer no-undo.
define output parameter records_updated as integer no-undo.
define output parameter records_locked as integer no-undo.
define buffer item for item.
define buffer item_update for item.
define variable retry_count as integer no-undo.
for each item no-lock:
accumulate 1 (total).
records_read = (accum total 1).
retry_count = 0.
repeat for item_update:
find item_update exclusive-lock
where rowid(item_update) = rowid(item)
no-wait no-error.
if locked item_update then do:
if retry_count > 5 then do:
records_locked = records_locked + 1.
leave.
end.
retry_count = retry_count + 1.
do on endkey undo, leave:
pause 3 no-message.
end.
undo, next.
end.
if not available item_update then do:
/*If that matters you can code for it too*/
leave.
end.
item_update.Price = 1000.
release item_update.
records_updated = records_updated + 1.
leave.
end.
end.
end.
define variable items_read as integer no-undo.
define variable items_updated as integer no-undo.
define variable items_locked as integer no-undo.
run update_items(output items_read,
output items_updated,
output items_locked).
display items_read items_updated items_locked with side-labels 1 col.

Convert the decimal value to get the split binary in Progress 4gl

I have to make a program which has the output like this :
def var vbit as logical extent 64 initial "false".
def var x as char form "x(16)" /* to store the decimal input */
Input : 2220010000000000
convert the value into:
22=00100010
20=00100000
01=00000001
00=00000000
00=00000000
00=00000000
00=00000000
then if the binary is sorted,the output will be:
123456789012345678901234
00100010001000000000000100000000000000000000000000000000
from this binary, change the vbit [x] like on the image.
Thanks a lot for the answer.
This is a quick example, most likely not usable for production like enviroments...
As far as I know there are no built in functions or methods to create binary numbers. So I've borrowed a function from here:
http://knowledgebase.progress.com/articles/Article/P125416
I've modified the function to return the integers with a 4 digit format, this will work for this specific example but of course not for larger binary numbers.
DEFINE VARIABLE vbit AS LOGICAL EXTENT 64 NO-UNDO .
DEFINE VARIABLE cString AS CHARACTER NO-UNDO FORMAT "x(16)".
DEFINE VARIABLE cBinary AS CHARACTER NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
ASSIGN
cString = "2220010000000000".
FUNCTION getBinary RETURNS CHARACTER (INPUT piValue AS INTEGER):
DEFINE VARIABLE cReturn AS CHARACTER NO-UNDO .
DEFINE VARIABLE iReturn AS INTEGER NO-UNDO FORMAT "9999".
DO WHILE piValue > 0:
ASSIGN
cReturn = STRING( piValue MOD 2 ) + cReturn
piValue = TRUNCATE( piValue / 2, 0 )
.
END.
IF cReturn = "" THEN cReturn = "0".
iReturn = INTEGER(cReturn).
RETURN STRING(iReturn, "9999").
END FUNCTION.
/* Convert the string of integers into a binary format */
DO i = 1 TO LENGTH(cString):
cBinary = cBinary + getBinary(INTEGER(SUBSTRING(cString, i, 1))).
END.
/* Move the binary numbers into the boolean variable */
DO i = 1 TO LENGTH(cBinary).
IF SUBSTRING(cBinary, i, 1) = "1" THEN
vbit[i] = TRUE.
ELSE
vbit[i] = FALSE.
END.
/* Uncomment this to output
123456789012345678901234
00100010001000000000000100000000000000000000000000000000
*/
/*
DISP "123456789012345678901234" SKIP
cBinary FORMAT "x(70)" WITH FRAME fr1 4 DOWN WIDTH 90.
*/
/* Display the boolean variable in the specified format */
DISP vbit WITH FRAME fr2 SIDE-LABELS 4 COLUMNS WIDTH 90 25 DOWN.

Using frames in progress 4gl

Can anyone help me understand how to display the following pattern using a Progress 4gl frame:
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5.
I have tried like this:
DEFINE VARIABLE a AS INTEGER NO-UNDO.
DEFINE VARIABLE b AS INTEGER NO-UNDO.
DO
a =1 TO 5 WITH FRAME f:
DO
b = 1 TO a WITH FRAME f:
DISPLAY a SPACE SKIP.
PAUSE.
END.
END.
/* while displaying the answer is overwritten, how do I display the answer side by side? */
If you were sending your output to a file you could do it like this:
define variable a as integer no-undo.
define variable b as integer no-undo.
output to "output.txt".
do a = 1 to 5:
do b = 1 to a:
put b.
end.
put skip.
end.
output close.
Using DISPLAY and FRAME is not like text files or printers. When you create a frame and DISPLAY "A" in it you are defining a single position where the variable will be displayed.
Every time that you DISPLAY A the value will be placed in the same position.
You can make it a DOWN frame and move to a new line with each iteration of the outer loop but you will still only have one position per line.
define variable a as integer no-undo.
define variable b as integer no-undo.
do a = 1 to 5 with frame f:
do b = 1 to a:
display b with frame f.
end.
down with frame f.
end.
To have multiple positions you need multiple variables or an array or you can build a string (doydoy44's solution). Here is an example with an array:
define variable a as integer no-undo.
define variable b as integer no-undo.
define variable c as integer no-undo extent 5 format ">>>>".
do a = 1 to 5 with frame f:
do b = 1 to a:
c[b] = b.
end.
display c with frame f.
down with frame f.
end
I'm not sur to understand what is the problem.
May be this can help you:
DEFINE VARIABLE a AS INTEGER NO-UNDO.
DEFINE VARIABLE b AS INTEGER NO-UNDO.
DEFINE VARIABLE woutput AS CHARACTER NO-UNDO.
DO
a =1 TO 5 WITH FRAME f:
woutput = "".
DO
b = 1 TO a WITH FRAME f:
woutput = woutput + " " + string(b).
END.
DISPLAY TRIM(woutput) SKIP .
PAUSE.
END.
The behaviour you are talking about is what I call a down frame. ABL creates a frame automatically for any output, and if you are displaying a series of records from a table, it knows to make that frame a down frame, for example:
for each customer no-lock:
display customer.
end.
But in your example you aren't using for each. To get the down frame behaviour you are going to have to make it happen yourself.
Here is the simplest code that will give you that:
def var v-i as int no-undo.
do v-i = 1 to 10 with down:
display v-i.
down.
end.
It's actually clearer what is going on, though, if you spell things out a bit further. Let's define a named frame, make it a down frame, and then use it:
def var v-i as int no-undo.
def frame f-x
v-i
with down.
do v-i = 1 to 10:
display v-i with frame f-x.
down with frame f-x.
end.
It's almost always worth defining a frame if you are outputting something, I find.
define variable a as int no-undo.
define variable b as int no-undo.
define variable res as char no-undo.
update a.
b = 1.
repeat while(b <= a):
res = res + " " + string (b).
b = b + 1.
disp res format "x(20)".
end.

Length of Array in progress?

Given two arrays, ( for example 1,2,3,4,5 and 2,3,1,0). Find which number of first array is not present in the second array. How can i get Length of Arrays in progress 4gl ?
If the object in question is an ARRAY, rather than a LIST, you use the EXTENT() function to determine the number of elements. Using arrays:
define variable a1 as integer no-undo extent 5 initial [ 1, 2, 3, 4, 5 ].
define variable a2 as integer no-undo extent 4 initial [ 2, 3, 1, 0 ].
define variable i as integer no-undo.
define variable j as integer no-undo.
define variable ok as logical no-undo.
do i = 1 to extent( a1 ):
ok = no.
do j = 1 to extent( a2 ):
if a1[i] = a2[j] then ok = yes.
end.
if ok = no then message a1[i] "is not in a2".
end.
To have the length of a list (number of items of the list), you can use NUM-ENTRIES() function.
To know if an item is present in a list, you can use LOOKUP() function.
So for your example, you can do something like this:
DEFINE VARIABLE wclist1 AS CHARACTER NO-UNDO INITIAL "1,2,3,4,5".
DEFINE VARIABLE wclist2 AS CHARACTER NO-UNDO INITIAL "2,3,1,0".
DEFINE VARIABLE wc-list-no-present AS CHARACTER NO-UNDO.
DEFINE VARIABLE wi-cpt AS INTEGER NO-UNDO.
/* For each items of list1 */
DO wi-cpt = 1 TO NUM-ENTRIES(wclist1, ","):
/* Test if the item is in list 2 */
IF LOOKUP(ENTRY(wi-cpt, wclist1, ","), wclist2, ",") = 0
THEN
wc-list-no-present = wc-list-no-present + "," + ENTRY(wi-cpt, wclist1, ",").
END.
/* TRIM is to remove the first "," */
DISPLAY TRIM(wc-list-no-present, ",").
def var a as int extent 5 initial [1,2,3,4,5] no-undo.
def var b as int extent 4 initial [2,3,1,0] no-undo.
def var i as int no-undo.
def var j as int no-undo.
loop:
repeat i = 1 to extent(a):
repeat j = 1 to extent(b):
if a[i] = b[j]
then next loop.
end.
Display a[i] "not in b array" format "x(20)".
end.

How to test if string is numeric using Progress 4GL

Does Progress 4GL have a function for testing whether a string is numeric, like PHP's is_numeric($foo) function?
I've seen the function example at http://knowledgebase.progress.com/articles/Article/P148549 to test if a character in a string is numeric. Looks like it has a typo, btw.
But I would think the language would be a built-in function for this.
I was looking at this myself recently. The approved answer given to this doesn't work in 100% situations.
If the user enters any of the following special string characters: ? * - or + the answer won't work.
A single plus or minus(dash) is converted to 0 which you may not want.
A single question mark character is valid value which progress recognises as unknown value at which again you may not want.
A single or group asterisks on their own also get converted to 0.
If you run the following code you'll see what I mean.
DISP DECIMAL("*")
DECIMAL("**")
DECIMAL("?")
DECIMAL("+")
DECIMAL("-").
The following additional code maybe useful to get around this
DEFINE VARIABLE iZeroCode AS INTEGER NO-UNDO.
DEFINE VARIABLE iNineCode AS INTEGER NO-UNDO.
DEFINE VARIABLE chChar AS CHARACTER NO-UNDO.
ASSIGN iZeroCode = ASC("0")
iNineCode = ASC("9")
chChar = SUBSTRING(cNumber,1,1).
IF NOT(ASC(chChar) >= iZeroCode AND ASC(chChar) <= iNineCode) THEN DO:
MESSAGE "Invalid Number..." VIEW-AS ALERT-BOX.
END.
Do not need a function can jsut do a straight conversion.
ASSIGN dNumber = DECIMAL(cNumber) NO-ERROR.
IF ERROR-STATUS:ERROR THEN
DO:
{Handle issues}
END.
or if it is always whole numbers can use INTEGER instead of DECIMAL.
The language does not have a built-in "isNum()" type of function.
An alternative to the kbase method would be:
function isNum returns logical ( input s as character ):
define variable n as decimal no-undo.
assign
n = decimal( s )
no-error
.
return ( error-status:num-messages = 0 ).
end.
display isNum( "123" ) isNum( "xyz" ).
This code handles any numeric strings - even if the used Character is longer than the max Decimal length etc.
FUNCTION isNumeric RETURNS LOGICAL (textvalue AS CHAR):
DEF VAR i AS INT NO-UNDO.
IF textvalue = ? THEN RETURN TRUE.
DO i = 1 TO (LENGTH(textvalue) - 1):
INT(SUBSTRING(textvalue, i, (i + 1))) NO-ERROR.
IF ERROR-STATUS:ERROR THEN RETURN FALSE.
END.
RETURN TRUE.
END FUNCTION.
Works 100% of the time
FUNCTION is-num RETURNS LOGICAL
(INPUT cString AS CHARACTER):
DEFINE VARIABLE iZeroCode AS INTEGER NO-UNDO.
DEFINE VARIABLE iNineCode AS INTEGER NO-UNDO.
DEFINE VARIABLE cChar AS CHARACTER NO-UNDO.
DEFINE VARIABLE iCount AS INTEGER NO-UNDO.
DO iCount = 1 TO LENGTH(cString):
ASSIGN iZeroCode = ASC("0")
iNineCode = ASC("9")
cChar = SUBSTRING(cString,iCount,1).
IF NOT(ASC(cChar) >= iZeroCode AND ASC(cChar) <= iNineCode) THEN DO:
RETURN FALSE.
END.
END.
RETURN TRUE.
END.