NewBie's Corner Session 27 Traversing A Global with $Order Part 1

NewBie's Corner Session 27 Traversing A Global with $Order Part 1

Welcome to NewBie's Corner, a weekly or biweekly post covering basic Caché Material.

Traversing A Global

Perhaps the most difficult concept in Caché/MUMPS is its Global Structure. This session and several that follow it deals with the Global Structure. However, just presenting the material will not guarantee your understanding of it. You must experiment with the data and concepts that are presented.

In this session, we are going to create a small Global of three levels deep, and then show the code to Traverse the Global.

The following code sets up the ^Trans Global and then gives the code to traverse it. I suggest you study the code, experiment with it until you have a good feel for how the Global is structured and how to traverse it.

 Kill ^Trans
 Set ^Trans("Cars","Chevy","Malibu")="4 Speed" 
 Set ^Trans("Cars","Ford","Fairlane")="Cheap"
 Set ^Trans("Cars","Ford","Mustang")="Fast"
 Set ^Trans("Cars","Toyota","Camery")="Nice"
 Set ^Trans("Cars","Toyota","Tercel")="Not as Nice"
 ZW ^Trans
 Set Sub1="" For  Do  Q:Sub1=""
 . Set Sub1=$O(^Trans(Sub1)) Q:Sub1=""
 . Write !,Sub1
 . Set Sub2="" For  Do  Q:Sub2=""
 . . Set Sub2=$O(^Trans(Sub1,Sub2)) Q:Sub2=""
 . . Write !," ==>",Sub2
 . . Set Sub3="" For  Do  Q:Sub3=""
 . . . Set Sub3=$O(^Trans(Sub1,Sub2,Sub3)) Q:Sub3=""
 . . . Write !," ====>",Sub3
 . . . Set Data=(^Trans(Sub1,Sub2,Sub3))
 . . . Write !," ======>",Data
 Quit

Please note that there are two spaces after the "For" and after the "Do".

In traversing the Global the $Order ($O) command is used, more on the $Order command in subsequent sessions. I suggest you do some reading on the $Order command, see the documentation at: http://localhost:57772/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_forder#RCOS_B70911

This is not the only method of traversing a Global. I will show different methods of traversing the Global in upcoming sessions.

--Mike Kadow

PaulMikeKadow@gmail.com

If you have a comment, please respond through the InterSystems Developer Community, don't send me private email, unless of course you wish to address me only.

See "Newbie's Corner Index" for an index of all NewBies' Posts

 

 

  • -1
  • 0
  • 597
  • 20

Comments

Hi Mike,

I really appreciate your Newbies work,  but I wouldn't use the Do <space><space> dot syntax.

It's a very obscure syntax, only used in Mumps. Using 'traditional' For-loops is much more in line with what today's programmers would expect.

Herman,

Thank you for your comment. But, like I said: "This is not the only method of traversing a Global. I will show different methods of traversing the Global in upcoming sessions."

However, if you have a favorite method of traversing a Global, please show us! I would love to see your 'traditional' For-loop.

Set Sub1=""
For  {
       Set Sub1=$Order(^Trans(Sub1))
      If Sub1="" Quit
      Write Sub1,!
      Set Sub2=""
      For  {
            Set Sub2=$Order(^Trans(Sub1,Sub2))
            If Sub2="" Quit
            Write ?3,Sub2,!
            Set Sub3=""
            For  {
                   Set Sub3=$Order(^Trans(Sub1,Sub2,Sub3))
                   If Sub3="" Quit
                   Write ?6,Sub3," = "
                   Set Data=(^Trans(Sub1,Sub2,Sub3))
                   Write Data,!
            }
       }
 }

Herman, very nice, thank you.

If you have no objection, may I use this in a subsequent NewBie's Corner?

 

To the Developer Community as a whole,

One of the reasons I show older techniques is that many times NewBies go into a group with older standards and ways of doing things. The NewBie needs to know the current techniques but also need to understand the older ways of doing thing. It is not much use to the NewBie to know all the current methods but cannot understand older style code.

You could improve that further by using the 3 parameter version of $order to fetch "Data". i.e:

Set Sub3=$Order(^Trans(Sub1,Sub2,Sub3))
If Sub3="" Quit
Write ?6,Sub3," = "
Set Data=(^Trans(Sub1,Sub2,Sub3))
Write Data,!

Becomes:

Set Sub3=$Order(^Trans(Sub1,Sub2,Sub3),1,Data)
If Sub3="" Quit
Write ?6,Sub3," = ",Data
Write Data,!

Jolyon, very nice indeed, thanks for chiming in.

Be careful when using the target parameter to $order. See the documentation for target.

If there is no data present at the node being iterated over then the target value will retain its last set value - to ensure you always use the correct value when set, kill the target variable before the $order statement and check it's validity before use with $data, e.g.

kill Data
Set Sub3 = $Order(^Trans(Sub1,Sub2,Sub3),1,Data)
quit:(Sub3="")
if ($data(Data)) {
    Write ?6,Sub3," = ",Data
    Write Data,!
}

I don't like the fact that u use for loops to traverse a global.
In most programming languages for loops are for when you know the number of iterations needed in advance.

 

see quote below straight from stackoverflow:   http://stackoverflow.com/a/2950945

The main difference between the for's and the while's is a matter of pragmatics: we usually use for when there is a known number of iterations, and use while constructs when the number of iterations in not known in advance. The while vs do ... while issue is also of pragmatics, the second executes the instructions once at start, and afterwards it behaves just like the simple while.

I don't agree with you.

You might be right if you were talking about for-loops in the 'traditional' way:
For var=start:step:end { ... }

'While' would be very awkward, the condition would be Sub'="", but that would break the logic of the $Order which needs a Sub="" to start with.

The argumentless-For isn't really a For, it's more like a Loop statement, in fact that's what I asked for in one of my yearly wish-lists to ISC.

Loop {
    Do whatever you need to do
    Continue if needed
    Quit if needed
}

Furthermore I wouldn't use Stackoverflow as a authorative source (in any sense, but certainly not) for COS/Mumps.
I think there are far more COS/Mumps experienced developers in this gremium.

It would be very interesting to see if we could come up with some sort of 'For Each' construct.
Because that's what we are really interested in: For Each Subscript in ^Trans: do something or equivalent to $Query;
For Each DataNode in ^Trans do something

 

The argumentless-For  is the worst in showing intent of the code.

A quit statement can be anywhere in the code. There is no clear definition as to where to put the condition for ending the loop.

What is wrong with a while that shows clear intent:

struct = $ORDER(TABEL(""),1,data)
while (struct'="") {
  s struct = $ORDER(TABEL(struct),1,data)
}

I agree, that an argumentless-For is confusing in the given code. Therefore we prefer the following construction:

s Sub1="" f  s Sub1=$o(^Trans(Sub1)) q:Sub1="" d 
.w !,Sub1

The advantages:

  1. One line less
  2. There is only one $o
  3. Initialization, iteration code and break condition are in one line what helps with readability.

The very much more elegant forEach construct is precisely what the EWD 3 ewd-document-store JavaScript abstraction of Global storage provides you with.  See:

http://www.slideshare.net/robtweed/ewd-3-training-course-part-22-traversing-documents-using-documentnode-objects

For traversal within ranges, see:

http://www.slideshare.net/robtweed/ewd-3-training-course-part-23-traversing-a-range-using-documentnode-objects

See also:

https://github.com/robtweed/ewd-document-store

Rob

 

For each can be implemented via macros:

#define ForEach(%in,%gn) s gn%in=$na(%gn) s %in="" f { s %in=$o(@gn%in@(%in)) q:%in=""
#define EndFor  }

$$$ForEach(key, "^global")
    Write key,!
$$$EndFor

In my past life as a developer, I always tried to make my code as readable as possible for future maintainability.  There are always many ways to achieve the end result in COS, but your example seems pretty cryptic (at least to me!).  Indirection (@) was always a last resort since the meaning can get buried or isolated, making it more of a challenge to understand down the road.  But, of course, it's good to show examples of what's possible smiley

Rob, thank you for your pointer, which helps in understanding Globals.

I have several sessions prepared for Global access, and this is just my first one.

I try to keep each session small and straight forward, so not overwhelm the reader.

Also, diagrams are difficult to construct using this tool provided.

Evgeny, your link still points to localhost for some reason...