ABL inserting and displaying table data - progress-4gl

I apologies for this being a very simple question but as a first time user of ABL open edge and im stuck. I have enter values into a table like so
METHOD PRIVATE VOID POPULATETABLE ( ):
DEFINE VARIABLE I AS INTEGER.
DO I = 0 TO 100:
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="SMITH"
TEST.ORDER_NUMBER=I
TEST.ORDER="BOOKS"
TEST.COST=45.00
TEST.CUSTOMER_NAME = "JACKSON"
TEST.ORDER_NUMBER=I
TEST.ORDER="PAPER CLIPS"
TEST.COST=1.7.
ASSIGN TEST.CUSTOMER_NAME="JONES"
TEST.ORDER_NUMBER =I
TEST.ORDER="PENCILS"
TEST.COST=2.50
TEST.CUSTOMER_NAME = "TURNER"
TEST.ORDER_NUMBER = I
TEST.ORDER="PENS"
TEST.COST=0.7.
END.
END METHOD.
and I'm trying to display them using this
FOR EACH TEST:
DISPLAY TEST.COST TEST.CUSTOMER_NAME TEST.ORDER TEST.ORDER_NUMBER.
RETURN.
END.
However the result only shows the last row of data entered. can anyone help, I'm even unsure on whether the display function is right or the assign is.

The "return" in your FOR EACH is causing the code to leave the loop after the first record. Delete that statement and you'll see all the records.
FOR EACH TEST:
DISPLAY TEST.COST
TEST.CUSTOMER_NAME
TEST.ORDER
TEST.ORDER_NUMBER.
RETURN. /* this is why you're only seeing one record - */
/* get rid of this and you'll see all the records */
END.

I would avoid assigning an order# of 0. It's just asking to confuse people.
define variable i as integer no-undo.
do i = 1 to 100:
create test.
assign
test.order_number = i
test.customer = "smith" /* you need some way to get */
test.order = "books" /* actual data for the rest */
test.cost = random( 10, 100) /* of the fields... */
.
end.
And then review the orders with:
for each test no-lock:
display test.
end.

Yeah, all I needed was a create statement per each assign for each record and that worked. Thanks everyone, the working coded looks like:
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="SMITH"
TEST.ORDER_NUMBER=I
TEST.ORDER="BOOKS"
TEST.COST=45.00.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME = "TAYLOR"
TEST.ORDER_NUMBER=I
TEST.ORDER="PAPER CLIPS"
TEST.COST=1.7.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME="THOMPSON"
TEST.ORDER_NUMBER =I
TEST.ORDER="PENCILS"
TEST.COST=2.50.
CREATE TEST.
ASSIGN TEST.CUSTOMER_NAME = "TURNER"
TEST.ORDER_NUMBER = 2
TEST.ORDER="PENS"
TEST.COST=0.7.
FOR EACH TEST WHERE TEST.COST > 1.3 BY TEST.ORDER_NUMBER:
DISPLAY TEST.
END.

Related

4GL ABL Openedge loop through handle?

here is my current code
def var hbTT as handle.
for each Cust:
hbTT:buffer-create().
assign
hbTT::Name = Cust.Name
hbTT::address = Cust.Address.
end.
now what I want to do is to loop through hbtt. How can I do that?
I tried
for each hbTT:
/* Do something */
end.
the error I get is
unknown or ambiguous table hbTT. (725)
thank you
You won't be able to do a loop that way, as for each requires a static name.
Instead, try this:
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
create query hQuery.
hQuery:set-buffers(hbtt).
hquery:query-prepare('for each tt'). /* <-- Where tt is the original buffer name */
hquery:query-open().
hquery:get-first().
do while not hquery:query-off-end:
disp hbtt::name hbtt::address .
hquery:get-next().
end.

Preventing Function Overriding in Lua Table

In my program when two functions with the same name are defined for the same table, I want my program to give an error. What's happening is that it's simply just calling the last function and executing it.
Here's a sample code
Class{'Cat'}
function Cat:meow( )
print("Meow!")
end
function Cat:meow()
print("Mmm")
end
kitty = Cat:create()
kitty:meow()
The result of the execution is only: "Mmm"
Instead I want something like an error message to be given.
Unfortunately, __newindex does not intercept assignments to fields which already exist. So the only way to do this is to keep Cat empty and store all its contents in a proxy table.
I don't know the nature of your OOP library, so you'll have to incorporate this example on your own:
local Cat_mt = {}
-- Hide the proxy table in closures.
do
local proxy = {}
function Cat_mt:__index(key)
return proxy[key]
end
function Cat_mt:__newindex(key, value)
if proxy[key] ~= nil then
error("Don't change that!")
end
proxy[key] = value
end
end
Cat = setmetatable({}, Cat_mt)

Bracketed key value - limit OR foreach ... in OR?

Continuing my quest to convert .NET to Progress, I faced another challenge yesterday.
Our company bought time ago a .NET DLL to manage Excel document without the need to install Microsoft Excel. There is several functions that return a series of cells depending of the need.
The returned value is a class that implement IEnumerator interface in .NET.
The problem is that I cannot find a way to iterate trough the cells without getting the error:
System.ArgumentException: Row or column index is invalid or out of required range
Is there a way to in Progress to validate if X is inside of the extent range?
OR
Is there a way to iterate trough the array without knowing the upper limit of the array?
Thank you!
Sebastien
--- temporary solution ---
/* declaration */
DEFINE VARIABLE oCell AS CLASS GemBox.Spreadsheet.ExcelCell NO-UNDO.
DEFINE VARIABLE oRange AS CLASS GemBox.Spreadsheet.CellRange NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* load excel file */
...
/* retrieve a series of cells */
ASSIGN oRange = oWorksheet:Cells:GetSubrangeAbsolute(1,1, 2,2).
/* first cell */
ASSIGN i = 0.
ASSIGN oCell = ?.
ASSIGN oCell = oRange:Item[i] NO-ERROR.
/* validate cell is in the range */
DO WHILE NOT oCell EQ ?:
MESSAGE oCell:Value VIEW-AS ALERT-BOX.
/* next cell */
ASSIGN i = i + 1.
ASSIGN oCell = ?.
ASSIGN oCell = oRange:Item[i] NO-ERROR.
END.
I don't have access nor I can test this solution, but if it implements correctly the interface some solution like this one should work:
/* declaration */
DEFINE VARIABLE oCell AS CLASS GemBox.Spreadsheet.ExcelCell NO-UNDO.
DEFINE VARIABLE oRange AS CLASS GemBox.Spreadsheet.CellRange NO-UNDO.
DEFINE VARIABLE oEnumerator AS CLASS System.Collections.IEnumerator NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* load excel file */
...
/* retrieve a series of cells */
ASSIGN oRange = oWorksheet:Cells:GetSubrangeAbsolute(1,1, 2,2).
oEnumerator = oRange:getEnumerator().
DO WHILE oEnumerator:MoveNext():
oCell = CAST(oEnumerator:current,"GemBox.Spreadsheet.ExcelCell").
END.
If it doesn't work exactly like this, at least it should point you in the correct direction to use it.
From the web page I'd infer that the # of cols =
oRange:LastColumnIndex - oRange:FirstColumnIndex
and the # of rows is
oRange:LastRowIndex - oRange:FirstRowIndex
I'd think using
oCell = oRange:Item[Int32, Int32]
to get the item at the row, col position would work better instead of using a single element array element.

Dynamic array or resize extend?

This is a superfluous question. Is there any dynamic array or list in Progress 10.2B?
Example:
I create a base class called "InventoryTransaction". I read a MSSQL table from Progress and I would like to create an instance of InventoryTransaction class for each record found then add it to a "list/array" so I can later process them.
Is there something like MyArray:Add(MyItem) that will increase automatically the array size +1 then will add the instance of MyItem to the array?
I discovered the function EXTENT to set a size dynamically to an array but I do not know the count before reading all the transaction in the MSSQL table. Alternatively, I could execute a "select count(*) from MyTable" before reading all the transaction to retrieve the count and then extent the array.
Thank you!
Happy friday!
Sebastien
You can create "indeterminate" arrays. i.e.
define variable x as decimal extent no-undo.
An indeterminate array variable can be in one of two states: fixed or unfixed, meaning it either has a fixed dimension or it does not. An indeterminate array variable has an unfixed dimension when first defined. You can fix the dimension of an indeterminate array variable by:
Initializing the array values when you define the variable,
Using the INITIAL option
Setting the number of elements in the array variable
Using the EXTENT statement
Assigning a determinate array to the indeterminate array, fixing it to the dimension of the determinate array
Passing array parameters to a procedure, user-defined function, or class-based method, so that the indeterminate array variable is the target for the passing of a determinate array, fixing the indeterminate array to the dimension of the determinate array
Once fixed, ABL treats a fixed indeterminate array as a determinate array.
I just discovered progress.lang.object:
FILE: array.p
/* declaration */
DEFINE TEMP-TABLE arrITem
FIELD Item AS CLASS PROGRESS.lang.OBJECT.
DEFINE VARIABLE oItem AS CLASS Item NO-UNDO.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
/* create 10 products */
DO i = 1 TO 10:
CREATE arrItem.
arrItem.Item = NEW Item("Item_" + STRING(i), "Description_" + STRING(i)).
END.
/* display object information */
FOR EACH arrItem:
ASSIGN oItem = CAST(arrItem.Item,Item).
DISPLAY oItem:ItemNo.
END.
FILE: item.cls
CLASS Item:
DEFINE PUBLIC PROPERTY ItemNo AS CHARACTER
GET.
SET.
DEFINE PUBLIC PROPERTY DESCRIPTION AS CHARACTER
GET.
SET.
/* constructor */
CONSTRUCTOR PUBLIC Item():
END.
CONSTRUCTOR PUBLIC Item(
INPUT strItemNo AS CHARACTER
,INPUT strDescription AS CHARACTER
):
ASSIGN ItemNo = strItemNo.
ASSIGN DESCRIPTION = strDescription.
END.
END CLASS.
Thank you!
Sebastien
The short answer is - no, the 10.2B AVM doesn't allow you to dynamically resize an array.
The long answer is you could (a) add the object to a linked list of objects, or (b) create a temp-table with a Progress.Lang.Object field, create a new TT record for each object instance you want to track, and assign the object's pointer to the TT's PLO field.

Using "who" variable list to open cell array

I am trying to cycle through a list of variables I have say 30+ and calculate the maximum and minimum value for each column in each variable. Save this in a new array and then export to excel.
My thoughts were to use the who function to create an array with the name of all variables which are present. Then cycling through each one using a for loop after working out the size of the array which was created. This works fine, however when I try and use the string to reference the array it does not work.
I will add in the code which I have written hopefully someone will be able to come up with an easy solution :).
variable_list = who
cell2 = input('What cell size do you want to look at? ');
STARTcell = input('What was the start cell size? ');
[num_variables, temp] = size(variable_list);
for va = 1:num_variables
variable = variable_list{va}
[max_value, max_index] = max(variable{cell2/STARTcell})
[min_value, min_index] = min(variable{cell2/STARTcell})
format_values{va} = vertcat(max_values, max_index, min_value, min_index);
end
The variables I am looking at are arrays which is why I use the cell2/STARTcell to reference them.
You need to use the eval() function to be able to get the value of a variable corresponding to a string. For example:
a = 1;
b = 2;
variable_list = who;
c = eval(variable_list{2});
results in c being 2. In your code, the following line needs to change from:
variable = variable_list{va}
to:
variable = eval(variable_list{va});
resulting in variable having the value of the variable indicated by the string variable_list{va}. If variable is of cell type, then you should be fine, otherwise you may have to revise the next two lines of code as well because it seems that you are trying to access the content of a cell.