[2009.1] $list questions: Best/optimal ways to remove empty list items and how to reverse?

157 views
Skip to first unread message

Micky Hulse

unread,
Jun 28, 2013, 2:18:18 PM6/28/13
to intersystems. public. cache
Hello,

First, example code:

<https://gist.github.com/mhulse/5886459>

That's my attempt at having code that converts a delimited string to a
list, trims leading/trailing whites pace and reverses the list (well,
actually, I reverse the string and then reverse each key in the loop).

So, I have code that gets the job done, but I'm left wondering ...

Are there any intrinsic Caché functions/classes/COS that would:

1. ... remove empty items from a $list?
2. ... reverse a $list?

In my example code, I cheat by reversing the string. Part of me thinks
it would be nice to reverse the $list keys rather than play games with
my strings. :)

Also, it would be much cleaner to do something like:

$emptyremoval($list)

I could not find anything in the docs or class reference (sorry in
advance if I overlooked something).

If there are no built-in options, does anyone have recommendations? :)

Thanks!
Micky

Micky Hulse

unread,
Jun 28, 2013, 5:20:34 PM6/28/13
to intersystems. public. cache
On Fri, Jun 28, 2013 at 11:18 AM, Micky Hulse <rgm...@gmail.com> wrote:
> If there are no built-in options, does anyone have recommendations? :)

... or, I'd be stoked to get feedback on my code. Is that how you
would reverse a list of items? Is that how you would remove empties?
I'm just mostly curious what the guru Caché coders would do. ;)

icargill

unread,
Jul 2, 2013, 3:15:39 PM7/2/13
to intersystems...@googlegroups.com
Micky
 
The only thing I would do is to cut out the overhead of all the string reversals. 
Just acheive the optional reversal by the way you build the Listblock.
e.g.
 
    set string = "dog goes ""bark"", duck says ""quack"", goose makes ""honks"", ,,,,,,,,,,,,,,,,cow goes ""moooo"", pig ""snorts"",,,The quick brown fox jumps over the lazy dog,," 
    ; Initialize:
    set delimiter = "," 
    set list = "" 
    set reverse = 1 // ... or "0" (boolean). 
    ; Loop over string using provided delimiter: 
    for i=1:1:$length(string, delimiter
    {
        ; Grab current item and trim the whitespace: 
        set temp = $zstrip($piece(string, delimiter, i), "<>W"
        ; If there's something left over, add it to the return value: 
        if ($length(temp))
        {
            set:('reverse) list = list_$listbuild(temp)
            set:(reverse) list = $listbuild(temp)_list
        }
    }

Micky Hulse

unread,
Jul 2, 2013, 3:22:09 PM7/2/13
to intersystems. public. cache
Hi Ian! Thanks so much for the reply!!!

On Tue, Jul 2, 2013 at 12:15 PM, icargill <ian.c...@e-dendrite.com> wrote:
> The only thing I would do is to cut out the overhead of all the string
> reversals.
> Just acheive the optional reversal by the way you build the Listblock.
> e.g.

Omg, that's way better of a solution! LOL. Thank you! :D

Not sure why I made things so complicated with string reversals and such.

Just to confirm, is this how you'd handle $list building with removal
of empties and forwards/reverse capabilities? I assume there's not
much better of a way to handle such things (i.e. there's no built-in
Caché/COS functions to do this stuff)?

Thanks again Ian, you just made my day! I owe you one (actually, I owe
you many)!!!

Cheers,
Micky
Message has been deleted

Rob vl

unread,
Jul 3, 2013, 5:11:30 PM7/3/13
to intersystems...@googlegroups.com
You could also ask yourself if its required to use a %List. Maybe it could be useful to extend the %ListOfDatatype class and add your own businesslogic to it. This way you can provide a constructor that accepts your string (and maybe a dynamic delimiter) and puts the data in its internal storage structure (array). This class is already itteratable in revesre order and you could also add a toSting(reverseOrder as %Boolean, delimiter as %String) as %String method that returns the content concatinated with your default or provided delimiter.

This is not a direct answer to your question but i don't know all your requirements.

Also keep in mind that using/creating a class that does all the magic is a good (or the best) way to re-use and, more important, maintain your code/businesslogic. It helps you to have a good marriage between "your app" and "the future" ;-)

Micky Hulse

unread,
Jul 3, 2013, 7:41:55 PM7/3/13
to intersystems. public. cache
Hi Rob! Thank you for the reply and the help, I really appreciate it. :)

On Wed, Jul 3, 2013 at 2:11 PM, Rob vl <robva...@gmail.com> wrote:
> You could also ask yourself if its required to use a %List. Maybe it could be useful to extend the %ListOfDatatype class and add your own businesslogic to it.

That's an excellent observation/comment.

In fact, at a certain point I am using an %ArrayOfDataTypes to hold
data I retrieve from my list. I also use GetNext() and GetPrevious()
at a later point in my program.

I have one method that takes the delimited string and converts it to a
$list (removes dupes, trims white space and, now, controls the
direction).

From there, I pass the list to my main block of code and itterate over
it to create the %ArrayOfDataTypes. Eventually, I have an option to
return that object using GetNext/Previous.

The problem I've encountered with my code is that I'm also
incorporating a "limit". I've found it can get tricky to limit &
reverse the return value.

Anyway, that's more info than you probably want to know.

My thinking was, if I can handle all the limiting, trimming, empty
removals and reversing as early on in the program as possible, then
it would make the rest of my code less complicated.

With all that said, you make a really good point. I'm going to go back
to drawing board and hash out a few alternative ways for me to work
with the %List/ArrayOf objects instead of the more primitive $list
operations.

> This way you can provide a constructor that accepts your string (and maybe a dynamic delimiter) and puts the data in its internal storage structure (array). This class is already itteratable in revesre order and you could also add a toSting(reverseOrder as %Boolean, delimiter as %String) as %String method that returns the content concatinated with your default or provided delimiter.

That's a great idea! Thank you for the tips! I needed this type of feedback. :)

> This is not a direct answer to your question but i don't know all your requirements.

For sure, I understand. I did not want to get too detailed as I did
not want to confuse anyone with the overall scope of my silly project.
:D

> Also keep in mind that using/creating a class that does all the magic is a good (or the best) way to re-use and, more important, maintain your code/businesslogic. It helps you to have a good marriage between "your app" and "the future" ;-)

More excellent advice! Thank you! :)

Thanks a million Ian and Rob, I rally appreciate you giving me your
professional advice (and example code).

Rob vl

unread,
Jul 4, 2013, 4:41:44 PM7/4/13
to intersystems...@googlegroups.com
Glad i could help.

Good that you say you want to do all those things as soon as possible in the flow. If you put the data provided by the client in your object, which handles all the formatting you need further on, straight away, you'll have a solid structure to handle that data in a consistant and clear way. Also future extensions or adaptations to your code are to be done only in that class.

I know, its a bit the same as my previous post but it is verry important to write code that is easy to use and maintain. The main things to do are:
1. write the code only once (so in one place (class))
2. make shure that your class has the least responsability as possible (in your case accept a string and provide methods to read/write data from/to that string).
3. use meaningful words as variable and methods
4. take enough time to document all your code. This is the part that most people skip to write as much "code" as possible.

That is what makes future maintenance faster and easyer to do. Ever had to maintain or use code someone else wrote without documentation and using carppy namings? Where the code was spread all over the place because it was faster to implement (write) it that way? Guess who is the black sheep if time passes and results stay out! Not the one who wrote it fast, crappy and without any documentation (and was lucky it all, kind of, worked). Crazy, i know, but the hard reallity.

But often its because there is a lot to do and learn and little time to do it all.

Good luck.

Mike Kadow

unread,
Jul 5, 2013, 8:49:20 AM7/5/13
to intersystems...@googlegroups.com
I agree with items 1-4 for maintainable code:
1) write the code only once (so in one place (class))
2) make sure that your class has the least responsability
3) use meaningful words as variable and methods
4) take enough time to document all your code.

But, may I add:
5) error trapping, processing and reporting
6) testing, testing, testing

-Mike Kadow

> Date: Thu, 4 Jul 2013 13:41:44 -0700
> From: robva...@gmail.com
> To: intersystems...@googlegroups.com
> Subject: Re: [Caché] Re: [2009.1] $list questions: Best/optimal ways to remove empty list items and how to reverse?
> --
> --
> Caché, Ensemble, DeepSee
>
> ---
> You received this message because you are subscribed to the Google Groups "Caché, Ensemble, DeepSee" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-publi...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Micky Hulse

unread,
Jul 5, 2013, 1:26:55 PM7/5/13
to intersystems. public. cache
Thank you Rob, Ian and Mike for the replies! I really do appreciate
all of the pro tips, advice and code. This is the type of
information/feedback I can't get anywhere else, so to me it's
invaluable. :)

On Thu, Jul 4, 2013 at 1:41 PM, Rob vl <robva...@gmail.com> wrote:
> ... snip ...
> But often its because there is a lot to do and learn and little time to do it all.

Hehe, even as a noob Caché guy, I get what you're saying.

I'm sure I've written my fair share of unmaintainable code.
Fortunately, I don't think there's anyone that's ever had to pick up
the pieces behind me (yet). :D

I mentioned this in another thread, but here's the specific code I'm working on:

"Delim"
<https://github.com/registerguard/delim>

I have yet to tweak the code based on the stuff we've talked about in
this thread, but I thought I'd share ... I think I do an OK job at
commenting code and keeping things clean.

My variable/class names might need some improvement, but overall I
like to think that I've done a decent job at keeping things clean and
organized.

With that said, feel free to burst my bubble with constructive feedback. ;-)

On Wed, Jul 3, 2013 at 1:22 PM, icargill <ian.c...@e-dendrite.com> wrote:
> No, not really. If you could guarantee no spurious spaces, e.g. ,,,,,,,,
> rather than ,,, , , ,,

Yah, I know my example is a little strange. I'm just expecting a human
to create the string, and I'm trying to account for very rare corner
cases. I'm sure that 99.9% of the time, I'd never get a string that
had empty delims with spaces in them, but I just wanted to make sure
my code could handle such a situation.

On the other hand, if I document well (for the end user) and say "DONT
put empty delims in string" (or something similar) then maybe I could
worry less and my code could be simplified.

> then you could reduce the duplicates with (I think the syntax is)
> set string=$ZStrip(string,"=",",")
> = means reduce duplicates

Ah, I did not know that about the "="! Cool! :)

I totally missed that when reading the docs:

"Caché ObjectScript Reference: $ZSTRIP"
<http://docs.intersystems.com/cache20091/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzstrip

Super cool!

Thanks again everyone! I owe you all an Oregon micro brew!!!!!! :D

Have an awesome day.

Cheers,
Micky

Rob vl

unread,
Jul 5, 2013, 5:17:40 PM7/5/13
to intersystems...@googlegroups.com
Just for the naming part of the code: "return" is now a keyword similar to "quit". So using "return" as a variable name you would have to "return return" :)

return, unlike quit", really returns from the method. Comes in handy in a loop. Don't have to quit the loop and then quit the method, just return the result from anywhere within the method. (Java style)

If the variable holds the result of your method, well... just name it "result" ;-) but it shows you take the time and effort to write good code. I have to check that @... stuff. Didn't know Caché supports that.

I don't know if there are any other functions for a %List that meet your requirements. I am not that much of a pro. I'm only writing COS for a year now. We mainly use the iKnow API (to process books) and store the data directly in globals. But %List is the type iKnow uses to provide its data.

I could also add a point 7 to the list: logging. But i had to write my own logging class because Caché, as far as i know, is not equipped with a logging framework. Mine is a little bit the SLF4J style (Java).

Keep up the good work (way) ;-)

Joel Solon

unread,
Jul 5, 2013, 5:58:19 PM7/5/13
to intersystems...@googlegroups.com
Re: Cache is not equipped with a logging framework...

Would Cache Auditing work for you?

From BlackBerry Q10
From: Rob vl
Sent: Friday, July 5, 2013 5:17 PM
Subject: Re: [Caché] Re: [2009.1] $list questions: Best/optimal ways to remove empty list items and how to reverse?

Gerd Nachtsheim

unread,
Jul 5, 2013, 6:07:46 PM7/5/13
to intersystems...@googlegroups.com
schrieb Micky Hulse, Am 28.06.2013 20:18:
> Hello,
>
> First, example code:
>
> <https://gist.github.com/mhulse/5886459>
>
> That's my attempt at having code that converts a delimited string to a
> list, trims leading/trailing whites pace and reverses the list (well,
> actually, I reverse the string and then reverse each key in the loop).
>
> So, I have code that gets the job done, but I'm left wondering ...

nice challenge...

> Are there any intrinsic Caché functions/classes/COS that would:
>
> 1. ... remove empty items from a $list?

set newlist = $lfs($zstrip($lts(oldlist,,1),"=",","))

> 2. ... reverse a $list?

Something like this....

listStuff
quit
Main(list) public
{
write !,"reverse and strip a list"
set:$g(list)="" list = $lb(1,2,3,4,,6)
w !,"input: "_$lts(list,",",1)
set rlist = $$reverseList(list)
w !,"reversed: "_$lts(rlist,",",1)
w !,"stripped & reversed: "_$lts($$stripList(rlist))
}

stripList(oldlist) quit $lfs($zstrip($lts(oldlist,,1),"=",","))

reverseList(oldlist="")
{
quit:(oldlist="")||('$listvalid(oldlist)) ""
set ptr = 0,
pos = $ll(oldlist)+1,
newlist = ""
while ($listnext(oldlist,ptr,val))
{ s $li(newlist,$i(pos,-1)) = $get(val) }
/* s newlist = $lb($get(val))_$get(newlist) */
quit newlist
}

result looks like this....


USER>d Main^listStuff($lb(1,,,4,"five",,,,"neun"))

reverse and strip a list
input: 1,,,4,five,,,,neun
reversed: neun,,,,five,4,,,1
stripped & reversed: neun,five,4,1
USER>


HTH

Gerd

Micky Hulse

unread,
Jul 7, 2013, 3:09:20 PM7/7/13
to intersystems. public. cache
Hi Rob! Thanks for the reply, I really appreciate it. :)

Sorry for my late reply ... The holiday weekend kept me away from my computer.

On Fri, Jul 5, 2013 at 2:17 PM, Rob vl <robva...@gmail.com> wrote:
> Just for the naming part of the code: "return" is now a keyword similar to "quit". So using "return" as a variable name you would have to "return return" :)

Oh, wow, good to know!

I assume this is new to 2013.1 (the below doc is not found in 2012.1)?

"Caché ObjectScript Reference: RETURN"
<http://docs.intersystems.com/cache20131/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_creturn>

I'll be sure to change my usage of the "return" variable in all future
code (I'm not liking the thought of using "return return"). :)

> return, unlike quit", really returns from the method. Comes in handy in a loop. Don't have to quit the loop and then quit the method, just return the result from anywhere within the method. (Java style)

Interesting.

I'm not sure if I'll ever get to use 2013.1. We're kinda stuck on 2009.1. :(

Yet another feature of later versions of Caché I'm looking forward to
playing with! :D

> If the variable holds the result of your method, well... just name it "result" ;-) but it shows you take the time and effort to write good code. I have to check that @... stuff. Didn't know Caché supports that.

Yah, I don't think Caché does. I just do it because I like the way
docblock style syntax looks (and I use it a lot of other langs). It's
probably more of a bad habbit on my part. I probably should stick to
writing doc comments that actually work well with the Caché system ...
This is probably a better example of code I've written that jives with
the way Caché automatically documents code based on comments:

<https://github.com/registerguard/custom.rg.Scraper/blob/master/scraper/custom.rg.Scraper.cls>

Now that you point it out, I just filed a ticket to fix this:

<https://github.com/registerguard/delim/issues/4>

Caché Documatic aside, I guess I also like the way it reads from a
code perspective ... but I probably should not apply docblock style
syntax to Caché stuff comments there's no support for it. :)

> I don't know if there are any other functions for a %List that meet your requirements. I am not that much of a pro. I'm only writing COS for a year now. We mainly use the iKnow API (to process books) and store the data directly in globals. But %List is the type iKnow uses to provide its data.

Interesting!

I've heard about iKnow/iFind. AMAZING technology. I'm really wishing
our site could get out of the past, as the stuff currently available
in Caché is looking awesome.

Also, your advice/tips have been very pro from my perspective ...
You're pro in my eyes. :)

I've been using Caché stuff for a while now, but mostly off/on. Also,
I'm more of a front-end guy (I guess), though I've been doing a lot of
more programming (vs. design) over the years, as it's a skill that's
harder to find (or, so I've been told). Unfortunately, I'm 100%
positive I'll never attain a high level of knowledge for when it comes
to Caché stuff ... Thankfully, I can try to fill in the gaps by asking
questions here and getting help from the pro coders like you and the
rest of the Caché/DTI gang. :)

> I could also add a point 7 to the list: logging. But i had to write my own logging class because Caché, as far as i know, is not equipped with a logging framework. Mine is a little bit the SLF4J style (Java).

Good tip! I'll see what kind of loggin I can add to my code. DTI has
some logging code that I can plug into ... The only drawback is that I
don't want my code to be DTI-only as I'm trying to write code that
non-DTI customers could use (not that anyone would use my code, but I
think it's good practice for me to try an separate things).

> Keep up the good work (way) ;-)

Thank you Rob!!!! It's much appreciated! :)

Micky Hulse

unread,
Jul 7, 2013, 3:24:53 PM7/7/13
to intersystems. public. cache
Hi Gerd! Thanks so much for the reply, and thanks a billion for the
example code, I really appreciate the help. It's great seeing how
experienced/pro Caché coders would handle the situation I've
presented. :)

On Fri, Jul 5, 2013 at 3:07 PM, Gerd Nachtsheim
<gerd.na...@intersystems.com> wrote:
> nice challenge...
>> Are there any intrinsic Caché functions/classes/COS that would:
>> 1. ... remove empty items from a $list?
> set newlist = $lfs($zstrip($lts(oldlist,,1),"=",","))

Man, that's cool! Until Ian had pointed it out, I did not realize
$zstirp had the "=" as an argument to remove duplicates.

I think I'm going to implement this code, as I don't think anyone will
input delims like:

"fooo, baz,,,,,,, xyz, , , ,,,,,,"

That above input string will probably never happen.

Real input strings might look like:

"foo, baz, bar, bling"

... and if anyone ever made it hard, the string might look like:

"foo,baz, bar,, bling, billy"

... but that's probably very unlikely (esp. if I document things well).

I've always been a fan of allowing people to do either this:

"foo, bar, baz, bing"

... or:

"foo,bar,baz,bing"

Where the former is more readable, but the former is allowed as well.

But, in order to make code simpler and more predictable, I should
probably enforce a certain style of delimited input string. :)

>> 2. ... reverse a $list?
> Something like this....
> ... snip ...
> result looks like this....
> USER>d Main^listStuff($lb(1,,,4,"five",,,,"neun"))
> reverse and strip a list
> input: 1,,,4,five,,,,neun
> reversed: neun,,,,five,4,,,1
> stripped & reversed: neun,five,4,1
> USER>

That's awesome!!!! Thanks for the demo code Gerd!!!! I can't wait to
play with it on Monday. :)

I'll be back once I've had time to dissect your code as I'm sure I'll
have questions.

Thanks again for the help, I greatly appreciate it! :)

Cheers,
Micky

Micky Hulse

unread,
Jul 7, 2013, 3:28:03 PM7/7/13
to intersystems. public. cache
Hi Joel,

On Fri, Jul 5, 2013 at 2:58 PM, Joel Solon <Joel....@intersystems.com> wrote:
> Would Cache Auditing work for you?

Is this the auditing you are referring to(?):

"Caché Security Administration Guide: Auditing"
<http://docs.intersystems.com/cache20131/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_audit>

Micky Hulse

unread,
Jul 7, 2013, 3:31:20 PM7/7/13
to intersystems. public. cache
On Sun, Jul 7, 2013 at 12:24 PM, Micky Hulse <mickyhul...@gmail.com> wrote:
> I've always been a fan of allowing people to do either this:
> "foo, bar, baz, bing"
> ... or:
> "foo,bar,baz,bing"
> Where the former is more readable, but the former is allowed as well.

That should say:

"Where the former is more readable, but the latter is allowed as well."

Joel Solon

unread,
Jul 7, 2013, 3:32:52 PM7/7/13
to intersystems...@googlegroups.com
Yes. It can be used for general purpose logging for apps. 

From BlackBerry Q10
From: Micky Hulse
Sent: Sunday, July 7, 2013 3:28 PM
To: intersystems. public. cache
Subject: Re: [Caché] Re: [2009.1] $list questions: Best/optimal ways to remove empty list items and how to reverse?

Micky Hulse

unread,
Jul 7, 2013, 3:37:21 PM7/7/13
to intersystems. public. cache
On Sun, Jul 7, 2013 at 12:32 PM, Joel Solon <Joel....@intersystems.com> wrote:
> Yes. It can be used for general purpose logging for apps.

Cool! And it looks like auditing has been around since at least 2007.1:

"Caché Security Administration Guide: Auditing"
<http://docs.intersystems.com/cache20071/csp/docbook/DocBook.UI.Page.cls?KEY=GCAS_audit>

Thanks for tip! :)

Rob vl

unread,
Jul 8, 2013, 5:54:53 AM7/8/13
to intersystems...@googlegroups.com
Hi Joel,

Thanks for the advice but...

The kind of logging i need is level based. I put a lot of logging messages in code but, depending on what i'm logging, use different levels.

When the app is tested and in production, the logginglevel should normally be only INFO and higher. My levels are (low to high) TRACE, DEBUG, INFO, WARN, ERROR. When i need to debug a specific class or method (this functionality must still be added) i can change the level for that class or method. This is useful to not have other classes put unnecessary log messages in my logs. So when something goes wrong i can change the level of logging and have a more detailed (and performance wise more intensive) view of whats happening through code. The class and method names are read from the stack.

I also have to check sometimes what level is logging to prepare logging details that are only used for that level of logging. This way i don't do any unnecessary and performance consuming logging preparations when there won't be any logging afterwards. The logging itself is done by macro's like $$$LOGINFO(message,details) where details is optional

If Auditing is able to do so then i did a lot of unnecessary work but when i looked it up i didn't find that functionality. If i missed something you can always let it know but i think that would be more something for an other thread.

Anyway, thank you for pointing it out.


Op vrijdag 5 juli 2013 23:58:19 UTC+2 schreef Joel Solon het volgende:

Rob vl

unread,
Jul 8, 2013, 6:07:34 AM7/8/13
to intersystems...@googlegroups.com

"The logging itself is done by macro's like $$$LOGINFO(message,details) where details is optional"

Sentence was not finished and needs to be:

The logging itself is done by macro's like $$$LOGINFO(message,details) where details is optional and can be almost anything (like string, array, %list, object...). The correct type of logging is handled in the logging object. It works pretty well.
Reply all
Reply to author
Forward
0 new messages