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
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
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.
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
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:
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:
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
"Perhaps the most difficult concept in Caché/MUMPS is its Global Structure."
A diagram or two might help get the concept of Global structure and the purpose of the $order function, as I've done here in explaining its cache.node implementation:
http://www.slideshare.net/robtweed/ewd-3-training-course-part-19-the-cachenode-apis
from slide 31
Thank you, Rob. I also like globals visualization which you can find in InterSystems Caché documentation.
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.
Hi, Mike! Thanks for the post.
For documentation links we can use links to online documentation:
http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_forder#RCOS_B70911
it will work on any device, even on mobile.
And looking forward seeing your example without dots and double spaces.
Evgeny, your link still points to localhost for some reason...
This is fixed, thank you!