Dynamic array or resize extend? - progress-4gl

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.

Related

Get a whole column in a struct field in MATLAB

I have a struct in MATLAB with the size 46x6, the fields are:
name, folder, date, bytes, isdir, datenum
Now I want all 46 entries of name. However, the MATLAB function getfield(structname, 'name') only returns the first entry.
How can I get all elements of the struct?
Name holds strings
If you want the results as a cell array you can call {structname(:).name}.
To return an array you can call [structname(:).name].
First I had to convert the Struct to a cell, and then access it with round brackets
tmp = struct2cell(mystruct)
tmp(1,:)
for i = 1:numel(structname)
name(i)= structname(i).name;
end

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.

MATLAB assign variable name

I have a variable called 'int' with alot of data in it. I would like to find a way to programically rename this variable with a user input. So I can query the user indentifcation information about the data, say the response is 'AA1', I want either rename the variable 'int' to 'AA1' or make 'AA1' a variable that is identical to int.
A problem using the input command arises because it allows the user to assign a value to an already created varialbe, instead of actually creating a variable name. Would using the eval function, or a variation of it, help me achieve this? Or is there an easier way?
Thanks!
Just for the record, int is a rather poor variable name choice.
That aside, you can do what you want as follows
say foo is the variable that holds a string that the user input. You can do the following:
% eliminate leading/trailing whitespace
foo = strtrim(foo);
a = regexp('[a-zA-Z][a-zA-Z0-9_]*',foo));
if numel(a) == 0
fprintf('Sorry, %s is not a valid variable name in matlab\n', foo);
elseif a ~= 1
fprintf('Sorry, %s is not a valid variable name in matlab\n', foo);
elseif 2 == exist(foo,'var')
fprintf('Sorry, %s already in use as a variable name.');
else
eval([foo,' = int']);
end
Assuming int (and now foo) is a structure with field named bar, you can read bar as follows:
barVal = eval([foo,'.bar']);
This is all somewhat clunky.
An alternative approach, that is far less clunky is to use an associative array, and let the user store various values of int in the array. The Matlab approach for associative arrays is Maps. That would be my preferred approach to this problem. Here is an example using the same variables as above.
nameValueMap = containers.Map;
nameValueMap(foo) = int;
The above creates the association between the name stored in foo with the data in the variable int.
To get at the data, you just do the following:
intValue = nameValueMap(foo);

Array of struct: set all empty attributes ([]) to NaN

I have an array of structs and would like to set all empty attributes to NaN:
structArray =
29x1 struct array with fields:
value
id
How do I set all struct.value attributes to NaN, if they are empty?
If they are empty the conversion [structArray.value] omits the empty elements...
Given this:
x(29).id = [];
x(29).value = [];
You can set the value of all .id fields like this
[x.value] = deal(nan);
To set only a particular subset of values define a mask of values to set and then use it in your assignment statement:
maskEmptyId = arrayfun( #(a)isempty(a.id), x );
[x(maskEmptyId).id] = deal(nan);
As #Pursuit explained there is an excellent way to replace the empty fields with NaNs
However, you may also be interested in a different approach.
Instead of replacement in hindsight you may be able to prevent the empty spots to occur in the first place. Assuming they are empty because nothing has been assigned to them, you can simply initialize your struct with NaNs.
For example:
structArray = struct ('id',[],'value',NaN)
Calling this before you assign anything to structArray would initialize the value field with NaN, but will still initialize the id to be empty.

objective-c variable length array global scope

is it possible to declare a variable length array with global scope in objective-c?
I'm making a game with a world class, which initializes the world map as a three dimensional integer array. while it's only a two dimensional side scroller, the third dimension of the list states which kinda of block goes at the coordinate given by the first two dimensions
after the initialization function, a method nextFrame: is scheduled (I'm using cocos2d and the CCDirector schedule method). I was wondering how to pass the int[][][] map array from the initialization function to the nextFrame function
I tried using global (static keyword) declaration, but got an error saying that global arrays cannot be variable length
the actual line of code I'm referring to is:
int map[xmax][ymax][3];
where xmax and ymax are the farthest x and y coordinates in the list of coordinates that defines the stage.
I'd like to somehow pass them to nextFrame:, which is scheduled in
[self schedule:#selector(nextFrame:)];
I realize I can use NSMutableArray, but NSMutableArray is kinda a headache for 3-dimensional lists of integers (I have to use wrapper numbers for everything...). is there any way to do this with integer arrays?
You can't have a statically allocated global array of dynamic dimensions in C (of which Objective C is a clean superset). But you can use a global array of any length or size (up to available memory) at runtime by using a global pointer, malloc, and array indexing arithmetic.
static int *map = NULL;
...
map = malloc (dim1 * dim2 * dim3 * sizeof(int)); // in some initialization method
if (map == NULL) { /* handle error */ } // before first array access
...
myElement = map[ index3 + dim2 * ( index2 + dim1 * index1 ) ]; // some macro might be suitable here
Or you could make Objective C getter and setter methods that checks the array and array bounds on every access, since a method can return plain C data types.
Another option, if you know the max dimensions you want to have available and are willing to use (waste) that amount of memory, is to just statically allocate the max array, and throw an exception if the program tries to set up something larger than your allowed max.
I tried using global (static keyword)
declaration, but got an error saying
that global arrays cannot be variable
length
But global array pointers can point to arrays of variable length.