Expression: get 3rd to last element of split(..) result

In an alarm Display Path binding, I want to use the folder name 2 parents below where the alarm resides, but am struggling to find information about how to do this. I can’t see a way to get the number of rows in a dataset using the expression language.

e.g. alarm tag path:
Winery/Receivals/Crushers/DC2001/Flush Valve/Alarms/Failed to Open

I’d like the display path to be:
‘Crusher 1 Flush Valve Failed to Open’
which means pulling out ‘Flush Valve’ from the tag path. I would like the expression to be as re-usable as possible so that I can use it in any location, and fix itself if ever the parent device folder (e.g. DC2001) is moved.

So far I have:
split({itemPath}, ‘/’)[row,0]
where ‘row’ needs to be replaced with psuedo-code (count-3) (’-3’ as index starts at 0)

Cheers,
Nick

1 Like

I don't have designer open right now but could you use concat?

concat(split({itemPath},'/')[2,0],' ',split({itemPath},'/')[3,0], ' ',split({itemPath},'/')[6,0])

The expression function len() (somewhat confusingly, placed in the ‘Strings’ section) works directly on datasets - so you can work backwards from the number of rows that way.

https://docs.inductiveautomation.com/display/DOC79/len

How about:

str = 'Winery/Receivals/Crushers/DC2001/Flush Valve/Alarms/Failed to Open'
print str.split('/')[-3:-2]

Thanks Paul, that’s the one I needed cheers.

@AlThePal, that’s python code, the question was about the Ignition-specific expression syntax.

Though you could use python code in an external function and call it from the expressions if you really want…

This is where I lean on the unique ability of objectScript() to efficiently execute a one-liner with access to an argument list. Like so:

objectScript("args[0].split('/')[-3:-2]", {Root Container.myStringProperty})
2 Likes

I was just about to post my question because I could not get it to work. Then I figured it out.

So FYI:
Custom Property holds my tag path

concat(“Trend - “, split({Root Container.UDT_Path},”/”)[1,0]," - “,split({Root Container.UDT_Path},”/")[3,0])

I just ran into a similar issue but I only wanted the last part of the tag path. I ended up using a lambda inside the runScript expression function. For anyone in the future this is what I used and it makes passing in arguments clear:

runScript("lambda str: str.split('/')[-1]", 0, {tagPath})

Be aware that invoking a script is less performant than using an expression. Although the expression is more awkward, it has a lot less overhead in the gateway. Always favour expressions over scripts

1 Like

As @nminchin said, I think you'll find this expression to be more performant. Just have to remember that the regex pattern in the split expression is selecting what will be removed from the string.

split("[providerName]a/path/to/tag/or/folder/withVeryLongName","[^\\s]+\\/")[1,0]

This will return withVeryLongName.

1 Like

I've never been real happy with how expressions deal with common sub-expressions. That is, not happy at all. The impetus to find a solution isn't there in Perspective, because you can put such expressions ahead of a transform so they will only be executed once. (And if you have more than one, my new asList() expression can bundle multiple such into a single value.)

But expression tags and Vision UI bindings do not have transforms. :frowning_face:

I realized while reading this topic that I've already created a combination that can do this, and using len() on the result of split() is the perfect test case:

forEach(
	asList(
		split({path.to.some.string}, '/')
	),
	it()[len(it())-1,0]
)[0]

The split() in the center of this expression is executed once, the result dataset becomes the one element in a list to be iterated, and therefore handed to both calls to it() within the loop (which runs just the once) to build the output list. Take the one element of that output list as the final output.

Yielding the last element of the path, but only referencing the string property once, and only running split() once.

:grin:

1 Like