What do You Use For Static Array Management in ObjectScript: List vs Global vs Local vs PPG

Solapas principales

Hi Developers!

Often we need to use relatively small arrays with constants, static arrays in algorithms, etc where we need to do something with each element of an array. There are several ways to deal with it in ObjectSctipt. 

Previously I used to use globals, locals, PPG for this but not so long time ago figured out that %List is a way too handy.

Indeed, suppose we have an array of months and need to set up and use it in our code.

Globals way:

Setup

ClassMethod MonthGlobalSetup(ad)

{

for i="January","February","March","April","May","June","July","August","September","October","November","December" {

s @ad@($I(@ad))=i

}

}

Usage:

ClassMethod MonthGlobalUsage()

{

set ad=$na(^Month) k @ad

d ..MonthGlobalSetup(ad)



// get the particular value by index

W "Month N5 is ",@ad@(5)



// work with all entries of the global level

set monthId=$Order(@ad@(""))

while monthId'="" {

w monthId," month is ",@ad@(monthId),!

set monthId=$Order(@ad@(monthId))

}

}

List way.  Setup:

ClassMethod GetMonthList() As %List

{

Return $Listfromstring("January,February,March,April,May,June,July,August,September,October,November,December")

}

Usage:

ClassMethod MonthListUsage()

{

set monthList=..GetMonthList()



// get the particular value by index

w "Month N5 is ",$Listget(monthlist,5)



// work with all entries of the list

set iter=0,id=0

while $Listnext(monthList,iter,month) {

w $seq(id)," month is ",month,!

}

}

Lists usage looks more readable and doesn't set into any Persistent global in Namespace which is needed to be documented. 

And 'While' iteration through all the values is much clearer with lists.

My preference now for static arrays is List.

What's yours?

 

What do you use to handle static arrays in ObjectScript

Regístrese o inicie sesión para participar en la encuesta.

Resultados
40%
(2)
Lists
20%
(1)
Globals
40%
(2)
Something else
  • + 1
  • 1
  • 233
  • 14

Comentarios

Why

ClassMethod GetMonthList1() As %List
{
    Quit $Listfromstring("January,February,March,April,May,June,July,August,September,October,November,December")
}

instead of

ClassMethod GetMonthList3() As %List
{
 Quit $lb("January","February","March","April","May","June","July","August","September","October","November","December")
}

Ok.

Parameter is about 6x faster btw:

ClassMethod GetMonthList1() As %List
{
    Quit $Listfromstring("January,February,March,April,May,June,July,August,September,October,November,December")
}

ClassMethod GetMonthList2() As %List [ CodeMode = expression ]
{
$Listfromstring("January,February,March,April,May,June,July,August,September,October,November,December")
}

ClassMethod GetMonthList3() As %List [ CodeMode = expression ]
{
$lb("January","February","March","April","May","June","July","August","September","October","November","December")
}

Parameter GetMonthList = {$lb("January","February","March","April","May","June","July","August","September","October","November","December")};

ClassMethod Time2()
{
    for method = 1:1:3 {
        set start = $zh
        
        for i=1:1:10000 {
            set result = $classmethod(,"GetMonthList" _ method)
        }
        
        set end = $zh
        
        write $$$FormatText("Method: %1, time: %2", "GetMonthList" _ method, end - start),!
        
    }
    
    set start = $zh
    
    for i=1:1:10000 {
        set result = ..#GetMonthList
    }
    
    set end = $zh
    
    write $$$FormatText("Parameter, time: %1",  end - start),!
}

Results

Method: GetMonthList1, time: .007255
Method: GetMonthList2, time: .006717
Method: GetMonthList3, time: .003878
Parameter, time: .000605

I like usage of parameter, as it is computed once -- at compile time. And after you only fetch value in cycle.

You can also use $listfromstring via the same technique.

Interesting that I can use $LB in a parameter declaration, but cannot call a method - e.g. if you need some logic to form a list. This is not compilable in this case.

Cool. And what is better with while $listnext from the performance point of view - local variable or refer to a parameter every time?

It depends on a use case.

  • For access by position use $lb.
  • For access by key use locals.

Consider the scenario of iterating over the result set. You need to do two things:

  • Convert value based on a column datatype
  • If a value is special translate it

To solve the first one (and assuming we get values by position number) the most efficient solution would be iterating over metadata once and building something like this:

set columns = $lb("int", "bool", "str", ...)

Then in each row we can easily get datatype by:

for i=1:1:colCount {
  set dataType = $lg(columns, i)
  set value = rs.Get(i)
}

Now for a second part - value translation, locals are great here. We prep our translator:

set local(1) = "a"
set local("hi") = "world"

and replace the value if needed

set:$data(local(value), newValue) value = newValue

Thanks, Ed! Two remarks:

1. You mention iteration over resultset and introduce an example of iteration over array of columns cortege or record.

2. My question is not about getting values from arrays like list, locals, ppg, globals but what is better to use when you have a case of full scan over the array that in some cases lists are handier vs locals etc. 

Though the question of what array is better when you need to get values for keys is interesting too, maybe it's another good topic for discussion. Answering your statement here I agree for LB the choice for value by position if it's not a very large (huge) array, where the global is the only answer.

1. Before we iterate over rows we need to determine column type, the example is about that.

Here's an example of what I'm talking about.

2. You want to avoid full scan as much as possible. Two techniques for that are:

  • knowing position
  • knowing key

And that determines the type of structure used.

If you don't know either you need to think about possible algorithm improvements.

2. Sometimes you WANT and NEED to do the full scan. And my topic was about these cases. In some programming languages, we even can find reserve words for such, e.g. for each.

And again your comment is interesting and helpful and deserves another topic too.

I agree that there are cases where we need to do something over each element of the collection.

But you said:

constants, static arrays in algorithms, e.g. arbitrary dictionaries

Which implies that foreach iteration is not desired here.

Anyways, in that case what does matter is size.

If you're sure that it would be less than $$$MaxStringLength then you can use $lb, otherwise use locals (which can be converted to ppg/globals easily).

Which implies that foreach iteration is not desired here.

Got you, this was the word "dictionaries". Removed it and added the comment about each element of array.

If you're sure that it would be less than $$$MaxStringLength then you can use $lb, otherwise use locals (which can be converted to ppg/globals easily).

Yes. Thanks.

For me the main acceptable case of foreach iteration is business objects - for example we receive array of results and must process them. In that case we, of course, must iterate over them, no way around it.

Everything static: constraints, enums, dictionaries, etc... should be (and usually could be) used without foreach iteration.