Short-hand syntax for anonymous arrays or objects initialization

Started by Stéphane Devouard, September 11, 2020, 05:16:39 AM

Previous topic - Next topic

Stéphane Devouard

In PHP you can create and initialize an array with :
$myArray = [ "item", "another item", "a third item"];

You can also pass either an indexed or associative anonymous array to a function using :
myFunction([
  "key1" => "value 1",
  "key2" => "value 2"
]);


Almost same in C# :
string[] myArray = { "value", "another value" }; // declaring and initializing an array
myFunction(new[] {10, 20, 30}); // passing an anonymous array to a function


In Javascript you can create a simple object (and manipulate it with a syntax which is basically that of an associative array)
let options = {
  url: "server:port/path/to/resource",
  timeout: 5000,
};
myFunction({
  mailFrom: "me@myisp.com",
  mailTo: "you@yourisp.com",
  message: "Hello there"
}); // passing an anonymous object


Almost the same in C#
myFunction(new { TextProperty = "Some Value", NumProperty = 100 });

In summary :

You do not always need to declare a variable -- you can already pass literal string or number values to a subroutine or method in PxPlus.

It would be great if it was possible to do it with other data structures such as arrays, associative arrays and objects -- the latter could be wiped out when not needed anymore using the "FOR OBJECT/PROGRAM/..." housekeeping clauses
Stéphane Devouard
Portfolio | Work

Mike King

Here are a couple of simple tricks you can use in PxPlus to get similar functionality. 

To quickly create and load an array try:

dim Y$[*]
read data from "Mike,Was,Here",sep="," to Y${ALL}


Use JSON to define/load an associative array:

dim load y$ = "{'name':'Mike', 'eyes':'blue'}"

Use an object to pass data.  First create the following object and save as "data.pvc"

def class "data" accept properties
end def


Now define and use to pass information as in:

oData = new("data" for program)
oData'Param1=123
oData'Param2$="Hello world"
...


Now you can pass all that data simply by passing the object handle -- and you can use the 'data' object wherever you want and have any number of them each with a different set of properties.

As for creating 'unnamed' arrays, we currently don't have a method to do this but can take it under consideration.





Mike King
President - BBSysco Consulting
eMail: mike.king@bbsysco.com

Stéphane Devouard

#2
Mike

Thanks for the answer. I know about these small tricks.

I even used this construct in my days at Explorer :
with new("data")
.'numProp = 123
.'strProp = "Hi there"
someObject'someMethod(.'_OBJ)
drop object .'_OBJ
end with

We didn't have the FOR ... clauses for object housekeeping since we were stuck with V10

My point is elsewhere :
1. being able to pass an unnamed/anonymous array [ "Mike", "Was", "Here" ] to a function/call/method without having to declare a Y$[] array and parse a string to fill it
2. being able to pass an unnamed/anonymous associative array [ "name" => "Mike", "eyes" => "blue" ] to a function/call/method without having to declare a Y$ JSON string and parse it
3. being able to pass an unnamed/anonymous plain old pxplus object (popo ?) to a function/call/method without having to declare a class skeleton & loading it

Basically 2 & 3 are very similar (in Javascript you can get/set an object property values either with objectRef.propertyName or objectRef["propertyName"] and so if either one was possible ("litteral" associative array or object definition), that would be cool.

As a suggestion, you could provide a standard / popo class yourself (for exemple, *obj/data.pvc or *plus/obj/data.pvc or whatever name you see fit)
Then allow a syntax such as :


someObject'someMethod(new("*obj/data" for program with Param1=123, Param2$="Hello World"))

// in someObject.pvc
function someMethod(options)
! // the anonymous object instantiated above is received with the Param1 and Param2$ property values set
enter options
! // now do something with options'Param1 and options'Param2$
end function

Stéphane Devouard
Portfolio | Work

Mike King

Actually for PxPlus 2021 we have added (actually the work is done and the logic is in QA) the ability to include a WITH clause to CALL, PERFORM and any method call to pass in values by variable name.

For example:

myObject'Function(with Name$="Dora", Type$="Cat", Age=10, Color$="Calico" )

When the method is invoked the variables Name$, Type$, Age, and Color$ will have been pre-initialized with the values specified.

Similar logic for:

CALL "SomeProg" with Name$="Dora", Type$="Cat", Age=10, Color$="Calico"
PERFORM "SomeProg" with Name$="Dora", Type$="Cat", Age=10, Color$="Calico"


This pretty much gives you what you were looking for apart from the use of an associative array.

As mentioned, this has already been developed and is one of the many new features that we will be releasing next May with our PxPlus 2021 release.


(BTW: Syntax wise the WITH clause will be accepted in Update 1 of PxPlus 2020 however it will generate a run-time error as the logic will not be enabled until PxPlus 2021)
Mike King
President - BBSysco Consulting
eMail: mike.king@bbsysco.com

Stéphane Devouard

Mike

Interesting... This brings some questions

In the case of an object method, what if the variables you passed in are also properties ? Or maybe they HAVE to be declared as properties and you can set their values in the same line that you're invoking the method ? And are they passed by value or by reference ?

For CALL, same questions as the object method -- what if the variable name is also in the ENTER ?

For PERFORM that is pretty much like the GOSUB ... WITH that is alreay available

I guess they'll have to wait until next May... ;)
Stéphane Devouard
Portfolio | Work

Mike King

Yes -- you will have to wait till May, but in answer to your question the WITH option simply acts as a if a LOCAL directive was executed prior the start of the method, called or performed program.

The data is passed by value so its a one-way into the invoked logic.

As for methods, the variable can be the same name as a property or local variable and it simply temporarily overrides its value.
For a CALL if the variable was the same an ENTER variable, the ENTER setting would override -- same as if you had a LOCAL prior the ENTER.

We had considered requiring the developer to provide a list of which variables you could pass in to the code using the WITH option, but we figured since this is only a programmer option and not something a end-user could take advantage of the extra overhead made little sense.
Mike King
President - BBSysco Consulting
eMail: mike.king@bbsysco.com

James Zukowski

I was reviewing things for another topic, and stumbled across this one, with this comment:

Quote from: Mike King on September 11, 2020, 09:25:54 AM
To quickly create and load an array try:

dim Y$[*]
read data from "Mike,Was,Here",sep="," to Y${ALL}


I've tried doing this type of thing, but it doesn't seem to work. More specifically:
Quote-}read record (1,key=k$)Rec$
-}dim Fld$[*]
-}read data from Rec$,sep=sep to Fld${all}
-}?dim(read num(Fld$))
0
-}?Fld$[1]
0001
-}?Fld$[2]

-}?dim(read num(Fld$))
2
-}

Is there something I'm missing? I've been looking for something like this for a while.

Thanks!
James Zukowski
Sr. Developer - J&E

BRAND>SAFWAY
Brand Industrial Services

keith.mcbride

It seems to work if you pre-dim the array size:

0100 DIM y$[15]
0200 READ DATA FROM "a,b,c",SEP="," TO y${ALL}
0300 PRINT y$[0],'LF',y$[1],'LF',y$[2]
-;run
a
b
c

RobL

Pardon me jumping in..

Try loading the dynamic array like this:

Rec$="ZZZ"+sep+"YYY"+sep+"XXX"+sep
!
dim Fld$[*]
for element$ from Rec$
Fld$[*]=element$
next
!
print sub(rec(cpl("iolist Fld${all}")),sep,",")


Hope this helps!

Regards,

Rob Leighton

James Zukowski

Keith/Rob:
I've used both methods to load arrays. I noticed that Mike had used a dynamic array to do the loading in his example, but when I tried it, I only get 1 entry. Just looking for clarification.
Thanks for the ideas.
James Zukowski
Sr. Developer - J&E

BRAND>SAFWAY
Brand Industrial Services

RobL

Hi James,

I get the same result as you do, but here's something interesting.

The following code doesn't work:

begin
!
dim Fld$[*]
read data from "Mike,Was,Here",sep="," to Fld${all}
print Fld$[1]," / ",Fld$[2]," / ",Fld$[3]
-:end
-:run
Mike /  /
-:


But, with the addition of a line that prints the array elements, it does.

begin
!
dim Fld$[*]
print Fld$[1],Fld$[2],Fld$[3]
read data from "Mike,Was,Here",sep="," to Fld${all}
print Fld$[1]," / ",Fld$[2]," / ",Fld$[3]
-:end
-:run

Mike / Was / Here
-:


I'm not sure why printing these elements is causing them to be created in the array, but that seems to be what's happening.

Consider the following:

begin
dim Fld$[*]
for z=1 to 3
print Fld$[z]
print dim(read num(Fld$))
next
-:end
-:run

1

2

3
-:


Regards,

Rob Leighton

Mike King

Yes -- referencing the next element to be create in a dynamic array will cause it to be created.
Mike King
President - BBSysco Consulting
eMail: mike.king@bbsysco.com