Get method on array throws error

Hi Rules experts,

I have a very simple rule that should loop over an array to retrieve values:

rule "ListTest"
when 
     Item TestButton changed
then 
     var String myTest = "v1 v2 v3 v4 v5 v6 v7 v8"

     // create array from string
     var list = myTest.split(" ")

     // initialize counter var
     var Number loopCounter = 0
 
     // decleare single item in list
     var String listItem

     // break criteria in loop
     var boolean flag = true

     while(flag) {

          logInfo("LoopTest", "loopCounter: " + loopCounter + " size: " + list.size())

          listItem = list.get( loopCounter )

          logInfo("LoopTest", "listItem: " + listItem)
 
          loopCounter = loopCounter + 1

          if (loopCounter >= 7){
               flag = false
               }
          }

end

When I run this rule I see an error which I do not understand at all:

[INFO ] [org.eclipse.smarthome.model.script.LoopTest       ] - loopCounter: 0 size: 8
[INFO ] [org.eclipse.smarthome.model.script.LoopTest       ] - listItem: v1
[INFO ] [org.eclipse.smarthome.model.script.LoopTest       ] - loopCounter: 1 size: 8
[ERROR] [.model.rule.runtime.internal.engine.RuleEngineImpl] - Rule 'ListTest': An error occurred during the script execution: Could not invoke method: org.eclipse.xtext.xbase.lib.ArrayExtensions.get(T[],int) on instance: null 

From the logs I can see that I’m able to get the value for loopCounter = 0, but after the increment of loopCounter I get an error. When I address the elements with fixed values (e.g. list.get(5) ) it works.

I suspect that I’m doing some really sill mistake, but I have no idea what it could be.
Do you have any hint for me?

Best,
Jens

Your loopCounter needs to be an int type, not a Number.
Or rather, use it as an integer

listItem = list.get( loopCounter.intValue )

Cool, that was it.
What I still don’t understand is why it worked for loopCounter = 0. Datatypes in rules are still a mystery to me :wink:
Thank you so much!
Best,
Jens

Loopcounter = 0 creates an Integer. Rules DSL is very loosely typed, ignores type predesignations, etc. When asked to do sums, it works in Numbers

That’s all very well until you come up against java functionality that demands a particular type like get()

Now that you have it working, there are much better ways to accomplish the same thing.

straight for loop:

then
    val list = "v1 v2 v3 v4 v5 v6 v7 v8".split(" ")

    for(int i = 0; i < 8; i++){
        logInfo("LoopTest", "loopCounter: " + i + " size: " + list.size())
        logInfo("LoopTest", "listItem: " + list.get(i))
    }
end

for loop short hand:

then
    val list = "v1 v2 v3 v4 v5 v6 v7 v8".split(" ")

    for(i: 0..<8){
        logInfo("LoopTest", "loopCounter: " + i + " size: " + list.size())
        logInfo("LoopTest", "listItem: " + list.get(i))
    }
end

forEach:

then
    val list = "v1 v2 v3 v4 v5 v6 v7 v8".split(" ")
    forEach[listItem, i |
        logInfo("LoopTest", "loopCounter: " + i + " size: " + list.size())
        logInfo("LoopTest", "listItem: " + listItem)
    ]
end

Using a List to begin with:

then
    val list = newArrayList("v1",  "v2", "v3", "v4", "v5", "v6", "v7", "v8")
    forEach[listItem, i |
        logInfo("LoopTest", "loopCounter: " + i + " size: " + list.size())
        logInfo("LoopTest", "listItem: " + listItem)
    ]
end

If you need to put a delay in for each time through the while loop see Design Pattern: Looping Timers.

A while loop is probably about the worst approach to doing something like this. I only mention it in case you are working up to using this while loop for a real world problem instead of just exploring the language.

Hi Rich,
I absolutely agree with you, and thank you for great examples you’ve shared. I have started with a bad example :wink:
Best,
Jens