How to resume inner $order loop from given point?

A function iterates global ^data(a,b,c) with $order using 3 nested for-loops. At certain point, for example with a=10, b=20, c=30, the function exits and later has to resume the iteration from the same point. What could be easiest way for it? My solution looks too ugly.

  • 0
  • 0
  • 406
  • 10
  • 4

Answers

You could try using $QUERY to iterate the global and use $QLENGTH to determine how deep you are and $QSUBSCRIPT to determine what the subscripts are.

How about saving the values of a, b, and c in whatever way makes sense given the context - global, PPG, %-var, %session, or have the method return them, and then also make them arguments to the method.  When calling the method again, check for them in whatever way they were saved, and if not found initialize them to "".

If you do find them, you might have to "rewind" each subscript by one to make sure you don't miss anything, which is easy to do with $ORDER:

set a = $ORDER(^data(a), -1)

>which is easy to do with $ORDER

Tried to do this but solution did not look easy at all :) 

It could be something similar to this:

Method Function(a1 As %String, b1 As %String, c1 As %String) As %String [ ZenMethod ]
{
   i (a1'="")&(b1'="")&(c1'="") a=a1,b=b1,c=c1 1
   s (a,b,c)=""
1 a=$o(^data(a)) g:a="" g:a=a1 2
2 b=$o(^data(a,b)) g:b="" g:b=b1 3
3 c=$o(^data(a,b,c)) g:c="" g:c=c1 q
q a1=a,b1=b,c1=c
    (a1_"|"_b1_"|"_c1_"|")
}

You have to make some changes especially on the first line to meet your needs.

Method Function(a1 As %String, b1 As %String, c1 As %String) As %String [ ZenMethod ]
{
(a1'="")&(b1'="")&(c1'="") a=a1,b=b1,c=c1 1
(a,b,c)=""
1 a=$o(^data(a)) g:a="" g:a=a1 2
2 b=$o(^data(a,b)) g:b="" g:b=b1 3
3 c=$o(^data(a,b,c)) g:c="" g:c=c1 q
    g 3
q a1=a,b1=b,c1=c
    (a1_"|"_b1_"|"_c1_"|")
}

Does it work for you? I think at least the order must be c->b->a, not a->b-c

This is a standard procedure to traverse (iterate over) data stored within a global.

Let’s say this is a global tree(the root is on the top). With

  1. s a=$o(^data(a)) g:a=”” q

we select the first index (a1) on the first level.

  1. s b=$o(^data(a,b)) g:b=”” 1

we select the first index (b1) on the second level.

  1. s c=$o(^data(a,b,c)) g:c=”” 2

we select the first index(c11) on the third level.

                g 3

going back to line 3, we select next indexes (c12 …c1m).

When c=”” we go to line 2 and select the next index (b2) on level 2

and again go to line 3 to iterate through the  third level-c21,c22 …cl etc.

When we finish the second level (b1,b2…bn) and b=””,

 we come back to line 1 and select the next index(a2 - not shown on the picture) on the first level etc.

The picture shows one tree but in common case this is forest of trees.(a1,a2……ak).

This is a standard procedure to traverse (iterate over) data stored within a global.

Let’s say this is a global tree(the root is on the top). With

  1. s a=$o(^data(a)) g:a=”” q

we select the first index (a1) on the first level.

  1. s b=$o(^data(a,b)) g:b=”” 1

we select the first index (b1) on the second level.

  1. s c=$o(^data(a,b,c)) g:c=”” 2

we select the first index(c11) on the third level.

                g 3

going back to line 3, we select next indexes (c12 …c1m).

When c=”” we go to line 2 and select the next index (b2) on level 2

and again go to line 3 to iterate through the  third level-c21,c22 …cl etc.

When we finish the second level (b1,b2…bn) and b=””,

 we come back to line 1 and select the next index(a2 - not shown on the picture) on the first level etc.

The picture shows one tree but in common case this is forest of trees.(a1,a2……ak).

Vladimir, the question was to resume the iteration from given point. Say we have only two indexes, A and B. When resuming, the code first has to check next values of B and only then next values of A. Your code stars with A skipping all remaining values of B.

Check $query-based solution in my answer to a post below. Looks to me as more elegant than with nested loops.

Hello

Just call the method in the middle of the looping so it will not be broken and will continue to traverse after the method return.

 Set Var1=""
 For {
 Set Var1 = $Order(^GLOBAL(Var1))
 Quit:Var1=""
 Set Var2=""
 For {
 Set Var2 = $Order(^GLOBAL(Var1,Var2))
 Quit:Var2=""
 Set Var3=""
 For {
 Set Var3 = $Order(^GLOBAL(Var1,Var2,Var3))
 Quit:Var3=""
 Do ##Class(name.class01).namemethod(Var1,Var2,Var3)
 }
 }
 }

Comments

My reworked solution so far. Looks working on samples I tested

    ; Get log for given 'clientid'
    ; 'maxlog' records max, starting from given 'date','dek','idx'
GetXLog(clientid,date,dek,idx,maxlog) public
{
    s:maxlog="" maxlog=400
    s counter=0
    s gl=$name(^cxlog(clientid,date,dek,idx)) ; ^cxlog("EFRW1000",20161219,2,1,"date")=20161219

    for { q:counter=maxlog
        s oldroot=$name(@gl,4)  ; we care only about first 4 indexes
        s gl=$query(@gl)    ; take next global
        s root=$name(@gl,4)
        continue:oldroot=root ; skip all sub-globals        

        q:clientid'=$qsubscript(root,1) ; for given 'clientid' only

        w root,!
        s counter=counter+1
    }

    w !
}

According to your code you know the different subscripts and want to continue on the same clientId and you only care for the first 4 subscripts, so try this (running only on the given clientid so no need to check it, and running only on the first 4 subscripts so no need to check if we are beyond the (or on the same) 4th subscript), haven't tested it!!:

    ; Get log for given 'clientid'
    ; 'maxlog' records max, starting from given 'date','dek','idx'
GetXLog(clientid,date,dek,idx,maxlog) public
{
    set:maxlog="" maxlog=400
    set counter=0
    ; ^cxlog("EFRW1000",20161219,2,1,"date")=20161219
    ;
    ;setting the subscripts for their previous subscripts so the next iteration will get to the values you asked for
    ;if you want to continue after idx and not to repeat idx then comment the next line
    set idx=$order(^cxlog(clientid,date,dek,idx),-1)
    set dek=$order(^cxlog(clientid,date,dek),-1)
    set date=$order(^cxlog(clientid,date),-1)
    for {
        quit:counter=maxlog
        set date=$order(^cxlog(clientid,date)) quit:date=""
        for {
            quit:counter=maxlog
            set dek=$order(^cxlog(clientid,date,dek)) quit:dek=""
            for { 
                quit:counter=maxlog
                set idx=$order(^cxlog(clientid,date,dek,idx)) quit:idx=""
                write idx,!
                set counter=counter+1
            }
        }
    }
    w !
}

Right.  Somehow I missed the evident idea that all the indexes can be simply initialized with -1 $order. Thanks. 

The only thing, last index should be not set to its previous value.