Expression Language Misunderstanding: indexOf()

I have the following Expression Structure:

{
"Valves":[
0:Open
1:Closed
2:Closed
...
]

I want to return true if any of the valves are open

When I do the following:

{value}["Valves"][0] = "Open"

Result: true

When I do the following:

indexOf({value}["Valves"],"Open")

Result: -1

Can someone explain what I am doing wrong or what assumptions I have that are incorrect? I also tried checking the typeOf({value}["Valves"][0] and it is a string...

{value}["Valves"] is not {value}["Valves"][0].

indexOf() does not find the index of a string in a list, it finds the index of a substring in a string.

What are you trying to accomplish?

I would use my Toolkit's where() iterator, wrapped in len(). Like so:

len(
    where(
        {value}['Valves'],
        it() = 'Open'
    )
) > 0

(I would also collapse your expression structure to use my asList() and nest it directly in the final expression. Avoid the transform overhead.)

Oh duh, that makes sense. I am looking for if any of the values are "Open" then I want to return True

Is a native clean solution not available?

No. My Toolkit closes many gaps in the native expression language, especially when dealing with lists, maps, and variable-length objects.

I'm bummed that there is no clean native solution. I am going to install your module to test it out, but I am unable to do a gateway restart at this time so I cannot test it out. Thank You for your work on this module.

Probably the best you can get in a single binding without Phil's module, is to add a script transform.

return any(valve == 'Open' for valve in value)

Phil's module is worth it, you will not know what you did without it.

:blush:

(Worth the bureaucratic trouble, if any, only, as it is free.)

am I missing something? I tried to recreate your issue and was unable to.

I created a property:

{
"valves":
[
"open",
"closed",
"closed"
]
}

I created a binding to that and ran this expression:

indexOf({value}['valves'], 'open')

it returns 0 and when I remove the string open from the list it returns -1, which is the intended result for what you described

this should natively work if you just need to check if it is contained in the list and not how many

I think the list behavior was added recently. @paul-griffith would know.

I don't think it's that recent because according to the 8.1 documentation indexOf | Ignition User Manual it says that it can take a list or tuple and it shows that documentation was last updated jan 13, 2025.

Interesting. "As of version 8.1.8". That was quite a while ago. Maybe there's a nuance/bug to expression structure bindings?

Yeah, it's expression structure bindings.

Most other contexts, if you're dealing with a list of values, it's a QualifiedValue over a list.
Expression structure bindings chained into a transform, for whatever reason, are a QualifiedValue containing a list of qualified values.
The indexOf function implementation unpacks the right hand side 'needle' into "Open", but doesn't touch the lhs/haystack, so the index checking doesn't work.

We already have code to unpack arrays of nested qualified values, we just need to add a branch for lists.

Ah. Run it through my unQualify() function.