Geek Tip: Learn Lists, Love Lists...
Category Technical
Bookmark :
You know about arrays, and the various kewl things you can do with them. Last time I did a geek blog story, I talked about some of the functions available for manipulating arrays - things like ArrayAppend, ArrayGetIndex, and so on. Well, in this edition of Geek Tips I am going to introduce you to a great, but underutilized feature in LotusScript - Lists. If you're a LotusScript Ninja, and are weilding Lists like nunchaku in your code, then move along and I'll post something for you to read tomorrow; however if you have no idea what I am referring to with the term "List", then this entry is for you.
What is a List anyway?
Lists are probably one of the most underutilized features in LotusScript. A List is similar to a one-dimensional array, in that it is a collection of indexed elements. However, Lists have a few differences that make them uniquely useful:
- Lists are indexed by a unique string value, not an index number. This means that you can reference things by a "name", rather than a number - this is very useful.
- Lists can be added to without the use of Redim. Redim called recursively, like when using Redim Preserve to build an array of indeterminate length, is expensive (speed-wise).
- You can remove elements from a list by using the Erase function. With a list the Erase function can remove individual elements; if you use Erase against an array, it blows away the whole array.
Lists are made up of two parts - the listtag and the value. The listtag takes the place of the index value in a traditional array. As stated earlier, each value stored in a list is referenced by a unique string, and this string is the listtag. Let's take a look at an example...
An Example
So let's say we have a list of National Football League (NFL) teams from the NFC South, and their respective quarterbacks (QBs). If we were to express this in a traditional array, it would look something like this:
Dim nfcsouth(0 to 3, 0 to 1) as String
nfcsouth(0, 0) = "Atlanta Falcons"
nfcsouth(0, 1) = "Mike Vick"
nfcsouth(1, 0) = "Carolina Panthers"
nfcsouth(1, 1) = "Rodney Peete"
nfcsouth(2, 0) = "New Orleans Saints"
nfcsouth(2, 1) = "Aaron Brooks"
nfcsouth(3, 0) = "Tampa Bay Buccaneers"
nfcsouth(3, 1) = "Brad Johnson"
If you wanted to determine who the QB of the Saints is, you would cycle through the array and find "New Orleans Saints" at the 0 position, and then return the 1 position (Aaron Brooks). This method works, but there is a better way - using a list!
This same list can be expressed as a list instead:
Dim nfcsouth List as String
nfcsouth("Atlanta Falcons") = "Mike Vick"
nfcsouth("Carolina Panthers") = "Rodney Peete"
nfcsouth("New Orleans Saints") = "Aaron Brooks"
nfcsouth("Tampa Bay Buccaneers") = "Brad Johnson"
Now retrieving the QB of the Saints is very easy - you simply pass the name of the team in the listtag (nfcsouth("New Orleans Saints")), which returns "Aaron Brooks". Much less code, much more intuitive.
A Small, Practical Example
Let's take a look at a more useful example. You know that @Unique has the ability to remove duplicate items from an formula list (an array in reality); but an equivalent feature doesn't exist in LotusScript in Release 5 (it does exist in ND6 in the ArrayUnique function).
Suppose you have built an array that may have duplicate items, and now you want to "clean up" the array by eliminating the duplicates. The obvious way to do this is to loop through the array and build another array without the duplicates. Doing this the traditional way would take many loops through the existing array, checking for a value to see if it was previously added. Even with some of the newer array functions, it would be somewhat tedious.
An alternative method is to use a list to gather unique values from the array. The list prevents duplicates from getting into the array, and eliminates the need for any REDIM PRESERVEs. Take a look at the function listed below:
Function Unique(arr As Variant)
REM I stole this code from Rocky Oliver's blog at www.LotusGeek.com
REM But he did say I could use it as long as I give him his props,
REM so it's kewl.
REM Copyright 2003, Rocky Oliver and Sapphire Oak Technologies
Dim data List As Integer
Dim i As Integer, n As Integer
REM test to see if a is an array; if not, return it
If Not(Isarray(arr)) Then
Unique = arr
Exit Function
End If
REM convert the values of arr to a string, then add them
REM to the list. Add the index number of the arr value as
REM the value of the list element - we'll use it in a minute
For i=Lbound(arr) To Ubound(arr)
data( Cstr(arr(i)) ) = i
Next
REM This takes the new list and puts it back into an array.
REM it uses the original array's index number, which is stored
REM as the value in the list, to retrieve the original value. Doing
REM this preserves the original data type of the array
n = 0
Redim newarray(0 To Ubound(arr)-Lbound(arr))
Forall z In data
newarray(n) = arr(z)
n = n + 1
End Forall
REM This returns the new array
Unique = Fulltrim(newarray)
End Function
Notice the comments. This function takes an array that is passed in, and adds it to a list called Data. the index number of the corresponding array value is added as the value of the Data list. Then this value is used to retrieve the array value for the "uniqued" array so that it preserves the original data type. Finally the array is trimmed to remove the empty array elements, and then is returned from the function.
Differences between lists and arrays
There are a few things you need to keep in mind about the differences between lists and arrays:
- You can't use UBound and LBound to determine the number of elements in a list. Instead you need to count the number of elements either when being added to the array, or using a Forall loop.
- You can't assign a list to a document item directly, like you can an array.
- Listtags must be strings, so make sure you convert the listtag value to a string (using CStr) if it isn't a string data type already.
These differences are easily accomodated, especially for the advantages lists offer you.
OK, that's kewl, but how do I know when to use a list?
Here are some general rules-of-thumb that you can use to determine when you should use a list:
- If you need to gather a list of items of indeterminate length, since you don't have to use REDIM to build the list - it is especially useful for gathering LARGE numbers of elements
- If you need to reference one value based on some string, such as Social Security Number by user name, product cost by product name, etc.
- If you need to total or count values referenced by a string, such as adding up the total sales of a list of products in a view
Conclusion
Lists are extremely useful. You owe it to yourself to learn more about them, practice using them, and understand the various ways that lists can be used. You'll quickly find that lists are an indespensible tool in your toolkit.
BTW, if you are a list pro, and can thing of other tips, tricks, suggestions, etc. about lists, please share them with the class, ok?
Enjoy your lists!
Rock
**Sex on television can't hurt you unless you fall off.







Blog Roll









Comments
I even wrote my own article some time ago - feel free to steal, etc:
http://www.billbuchan.com/newweb.nsf/b41aea976eaaa95980256cc7000e0114/220ff41ed212eeca80256cc7000f679f!OpenDocument
I must have written 20 or so "directory compare" routines over the years..
Now - if only we could get lists to easily (and quickly!) give us a count (such as myList.count), and give us out a sorted list.. Now *then* I'd be happy! (okay, I usually dump it to an array, sort, etc, etc)
---* Bill
Posted by Wild Bill At 10:14:33 AM On 10/31/2003 | - Website - |
As for the CPU, I haven't noticed it. Did you upgrade using a full install, or the incremental installers? I know that after you start Notes after an upgrade it goes and tries to upgrade design of templates - maybe that's what's happening. Is it pegging the CPU when you do certain tasks, or all the time? Right after you start the client, or after you do "something"?
Rock
Posted by Rock At 01:03:22 PM On 06/02/2004 | - Website - |
That's really good information - do you know what specific versions of Notes this problem exists? I just tested it in Notes 6.0.1, and the problem seems to be fixed. I ran the code, verbatim (except I dimmed the count and count2 vars - Option Declare and all that
So it would be great to know what versions this problem manifests itself. For instance, there was a leak in Do While loops on collections in all versions of Notes up through 5.0.3, and there was an Evaluate leak up through 5.0.6 (I think). So maybe this has been addressed in a later version of R5.
Anyone willing to test for us and report back here?
I love the power of collaboration!
Rock
Posted by Rock At 10:14:33 AM On 10/31/2003 | - Website - |
For the longest time, I didn't use lists (nor did I even know what they were). But after getting a quick lesson from Joe Litton based on some Rocky Oliver DevCon material, I quickly became a believer. I've seen list coding do some amazing things with very few lines of code. I love to use them to get counts of items in a collection of documents. For instance, say you want to programmatically know how many documents each person submitted in a database. Using lists with the user's name as the key, it's very simple to either start a counter for that person if it's their first document, or to increment their existing counter if you already have an entry for them.
Thanks for the writeup, Rocky!
Posted by Tom Duff At 10:14:31 AM On 10/31/2003 | - Website - |
Im glad I was able to discover this site...
I hope I can get help here...
I am really not good in lists / arrays but I am trying...
I am having a "type mismatch" error in my script.
I have a multivalued field. (separated by comma)
What I want to do is to get the current value of that field
and append a new value or list of values in the same field.
And i got this error "Type mismatch" on line
Call mi_doc.ReplaceItemValue("prodPurchased_C", curr_prodlist + "," + newprodlist )
I hope I can get some inputs here...
Thanks in advance for the assistance..
Machu
Posted by Machu At 12:40:13 AM On 08/10/2006 | - Website - |
Rock on!
Rock
Posted by Rock At 10:14:33 AM On 10/31/2003 | - Website - |
I can already see that I am going to have to buy Virtual PC for future use. That seems to be the way to go for future multi-client/server version development.
Rock
Posted by Rock At 10:14:31 AM On 10/31/2003 | - Website - |
Julian - Yeah, this machine does have quite a few versions of Notes. Can ya tell I'm a consultant and this is my development machine? Currently all the R5 flavours already mentioned (5.0.9a through 5.0.12), plus N6.01 CF1 and 6.5 Milestone 2 Beta. You just never know what version a client will have, and where some obscure bug turns out to be version specific.
Too bad it has become so tricky in N6 to install multiple versions. For some additional potholes, see section "III. The Windows Installer program does not verify directory locations during re-install or upgrade" on this technote:
http://www-1.ibm.com/support/docview.wss?rs=203&q=7003629&uid=swg27003629&loc=en_US&cs=utf-8&lang=en
Ouch. There's gonna be some carnage along the way (as there was with 6.5 M1 when people tried installing alongside N6). The whole process has gotten so convoluted it's a little crazy for people that need multiple versions on a single machine.
And in case you're wondering, I haven't tried running more than about three versions concurrently. In theory I could run 'em all at the same time. Hmmm, s'pose if I really wanted to go cross-eyed, I could open Notes, Designer and Administrator of each version... Now that'd be a fun taskbar...with just a touch of waffle overload.
- Rod
Posted by Rod At 10:14:32 AM On 10/31/2003 | - Website - |
I think you're right to be a list evangelist. They're sooo convenient to use sometimes.
I have had one problem with lists, though. At the risk of being rude and turning this comment into a "hey, look and see what I did on my site" link (I'm not even going to link back to my site), here's something I put in my "Unfinished LotusScript Book" regarding the problem.
Use Caution Passing Lists as Function or Sub Parameters
You should be very careful when you are passing lists around as function or sub parameters. Many versions of Notes have problems passing lists more than once as a parameter, and as a result, you can sometimes lose data or corrupt the list as the list is passed from the calling routine to the function or sub and back to the calling routine. Consider the following script:
Script ■ Data loss passing lists as parameters
Sub Initialize
Dim newlist As Variant
Dim listsize As Integer
newlist = CreateList
listsize = CountList(newlist)
End Sub
Function CreateList () As Variant
Dim thislist List As Integer
thislist(1) = 1
thislist(2) = 2
thislist(3) = 3
thislist(4) = 4
thislist(5) = 5
thislist(6) = 6
CreateList = thislist
End Function
Function CountList (thislist As Variant) As Integer
Forall stuff In thislist
count% = count% + 1
End Forall
Forall things In thislist
count2% = count2% + 1
Print "Forall loop #1 counted " & count% & _
". Forall loop #2 is counting " & count2% & "..."
If (count2% = 50) Then
Print "Forall loop #2 exiting because a count of " & _
count2% & " was reached."
Exit Forall
End If
End Forall
CountList = count%
End Function
If you compile this agent and run it, you will see that in the CountList function, the first loop through the list will return a count of 2, and the second loop through the list will loop forever if you don't stop it.
- Julian
Posted by Julian Robichaux At 10:14:31 AM On 10/31/2003 | - Website - |
i would like to ask who can help me regarding my dilemma.i have a field for request number that is computed when decomposed. its formula is @unique.
then i have to reuse rejected requests.i have a button that copies the selected document and create a new one..and with it i can change the values of some fields...but i need to change value of the request number.how can i assign same unique value to it in lotus script...my script change only the status(tsr_status) i havent put the changing of request numebr(tsrno) because i still have no idea ..here's my script
Sub Click(Source As Button)
Dim ws As NotesUIWorkSpace
Dim session As NotesSession
Dim db As NotesDatabase
Dim uiview As NotesUIView
Dim uicol As NotesDocumentCollection
Dim doc As NotesDocument, docNew As NotesDocument
Set ws = New NotesUIWorkspace
Set session = New NotesSession
Set db = session.currentDatabase
Set uiview = ws.CurrentView
Set uicol = uiview.Documents
If uicol.count = 0 Then
Msgbox "Please select a document to re-use.", 48, "Telecom Service Request"
Exit Sub
Elseif uicol.count > 1 Then
Msgbox "Please select only one document to re-use. ", 48, "Telecom Service Request"
Exit Sub
End If
Set doc = uicol.GetFirstDocument
Set docNew = New NotesDocument(db)
'Set docNew = ws.ComposeDocument("","","Combine")
Call doc.CopyAllItems(docNew,True)
docNew.tsr_status = "3"
docNew.SaveOptions = "1"
Call docNew.ComputeWithForm(False, True)
Call docNew.Save(True, False)
Call uiview.DeselectAll
Call ws.ViewRefresh
End Sub
Posted by Cel At 05:48:14 AM On 09/08/2004 | - Website - |
Posted by DanGerous Situations At 05:21:35 PM On 05/28/2004 | - Website - |
In the sample code Julian provided, I initially thought the ListTags could be the root of the problem as they are not strings:
thislist(1) = 1
thislist(2) = 2
and so on, would more correctly be:
thislist("1") = 1
thislist("2") = 2
No dice on that one as the infinite loop remained a problem in 5.0.9a. So much for that bright idea...
I then tried the code on 5.0.12, an it worked just fine. Same for 5.0.11, and 5.0.10. Guess that means there's a fix in 5.0.10. Brilliant deductive reasoning...
Sure enough, the fix list database on LDD contains an entry for a LotusScript List fix in 5.0.10:
http://www-10.lotus.com/ldd/r5fixlist.nsf/a8f0ffda1fc76c8985256752006aba6c/84afed24311fbf3485256b8000793980?OpenDocument
SPR# SCRL3NBPWL - Fixed an infinite loop which occurs if a list is passed into a static sub as a variant.
So there we have it. The problem Julian described is bound to happen in R5 thru 5.0.9a, and was fixed in 5.0.10. Is 4.x affected? I dunno as I haven't checked.
Thanks for the warning Julian. Glad to find out here rather than the hard way.
Posted by Rod At 10:14:34 AM On 10/31/2003 | - Website - |
Nice detective work Rod. Boy, you sure must have a lot of Notes clients loaded on your machine. 5.09a, 5.10, 5.11...
Rock, I think you're right -- this collaboration thing is bound to catch on.
- Julian
Posted by Julian Robichaux At 10:14:34 AM On 10/31/2003 | - Website - |