Stuart Byrne · Mar 10, 2020

Reducing the end variable in a FOR loop once executed

Dear All,

I'm looping through a list with characters (Parenthesis, square brackets etc) and I wish to remove these from the list.

The for loop and IF logic are effectively removing these items from a list.

However the variable I use for the maximum number of loops carries on beyond the number of items of the list after removal.

In the output value for these I get the integer of the for loop instead of a  null for the empty list fields.

I can see in the documentation that the end variable is not editable once the loop is started.

I looked at quitting out of the loop if the list value returned is null, but this isn't working either.

Any ideas please.



7 0 12 429



Unless you publish the failing code fragment, it would be difficult to help you.

A sample would be helpful, but have you tried using a WHILE loop instead? Without a sample, it is hard to give any more suggestions

Right now you're deleting items from the same list you're iterating through. As an alternative you could loop through the original list but in the loop you build a new list. Only insert an item into the new list if it doesn't contain the characters you're trying to remove.

Loop backwards from the tail to the head.


Or even better post your code.

Is speed/memory footprint important?

Hi Edward,

I've posted an excerpt of the code as I want to save the classmethod for another post.

Memory/speed shouldn't be an issue as the size of the lists should be <30 elements.



Excerpt of Code as Promised:

set list = "ABC~DEF~GHI~JKL~MNO~[~PQR~]"

set SegmentList = $LISTFROMSTRING(list,"~")
Set SegmentCount = $LISTLENGTH(SegmentList)
FOR i = 1:1:SegmentCount{
IF ($LISTGET(SegmentList,i,i)= "[") {
set $LIST(SegmentList,i,i)=""
set OptionalSegment = "Y" 
ELSEIF ($LISTGET(SegmentList,i,i)= "]") {
set $LIST(SegmentList,i,i)=""
ELSEIF ($LISTGET(SegmentList,i,i)= "") {
Write !, "Current Segment: "_$LISTGET(SegmentList,i,i)
Set SegmentFields = $LISTFROMSTRING(object.SegmentSubStructure,"~")
Set SegFieldCount = $LISTLENGTH(SegmentFields)

 One thing to note: the final ELSEIF is not going to be true ever since the third parameter of $LISTGET is the default value if the requested list value is undefined. Since i will never be = "", this will never quit.

As Marc mentioned, it may just be easiest to build a new list out of segments that aren't [ or ]

Hi Stuart

And using CONTINUE command ?

set list = "ABC~DEF~GHI~JKL~MNO~[~PQR~]"
set SegmentList = $LISTFROMSTRING(list,"~")
set SegmentCount = $LISTLENGTH(SegmentList)
set OptionalSegment="N"
FOR = 1:1:SegmentCount{
  if $LISTGET(SegmentList,i)= "[" set OptionalSegment = "Y" continue
  if $LISTGET(SegmentList,i)= "]" set OptionaSegment="N" continue
  write !, "Current Segment: "_$LISTGET(SegmentList,i)," ",OptionalSegment
  ;set SegmentFields = $LISTFROMSTRING(object.SegmentSubStructure,"~")
  ;set SegFieldCount = $LISTLENGTH(SegmentFields)

As Eduard Lebedyuk already wrote: If you want to work on a list you iterate through (esp. when removing entries) you best loop backwards.

for i = SegmentCount:-1:1 { ... }

Otherwise you will remove entries and later try to read them.



Hi Stuart,

As others have said, it's best practice to build a new variable, rather than amending what you have (there's some fancy name for the rule, I think, or maybe just "functional programming").

Looking at the string, I assume it is HealthShare HL7 message format, so the contents are pretty limited. In which case maybe a shortcut could be used:

s out=$Replace($Replace(list,"~]",""),"~[","")

Here's another alternative:

w $ZSTRIP($ZSTRIP(list,"*","[]"),"=>P")

I admit it could go horribly wrong with multiple nesting (I don't remember all the possible formats), so needs some testing. 

Hope you're keeping well,



if I were you I would also consider the %Regex.Matcher class ReplaceAll method to use.

For example:

set list = "ABC~DEF~GHI~JKL~MNO~[~PQR~]"

set matcher1=##class(%Regex.Matcher).%New("\[",list)

set matcher2=##class(%Regex.Matcher).%New("\]",matcher1.ReplaceAll("Y"))

write matcher2.ReplaceAll("N")