Searching and looping lists

Some months back there was a discussion on the Omnis lists on how to navigate lists in Omnis Studio effectively. That discussion lead to some interesting discussion given by several people and I've been wanting to summize them on my blog for some time but never got around to it. Even today I don't have time to do a proper writeup but since I just wrote a routine I wanted to share I've decided to do a quick post about these.

I specifically thank Kelly Burgess who gave some great tips on this subject

From the start of times Omnis has had a number of 4GL functions that allowed you to search and select lines in a list. In Studio the equivelent is in the method $search. Often the 4GL commands are still used because it isn't always clear how to implement them with $search and in some cases the 4GL functions can lead to be faster. However the 4GL commands can not always be used and there are certain things that can work even better with the $search. So below is a listing of some approaches that may help.

First, $search has 5 parameters:
1) the search itself which should be a calculation that result in true for the line(s) we're after. $ref can be used to point to the row being evaluated
2) search from start. If true we search from line 1 of the list. This allows us to find a line, do something, then find the next line. Tip: if the current line is 0 the list is always searched from the start
3) search selected lines only, if true only lines currently selected are evaluated
4) select matches, causes lines that match our calculation to be selected
5) deselect nonmatches, causes lines that do not match our calculation to be deselected

If either parameter 4 or 5 is ktrue $search evaluates all lines and returns the first line for which the selection has changed. If both are false $search will make the very first line it finds that matches the calculation current and returns that line number.

Finding a line that matches a certain criteria:

;  For finding a specific line, note that we need to specify all 5 parameters!
If lvMyList.$search($ref.MyColumn = 'FindMe',ktrue,kfalse,kfalse,kfalse)>0
; Found it, the current line is my mach
Else
; Sorry no such entry exists
End If

 

Selecting lines that match a criteria

;  The defaults for our columns work fine
Do lvMyList.$search($ref.MyColumn = 'FindUs')

 

Selecting all lines

Do lvMyList.$search(kTrue)

 

Deselecting all lines

;  Note that we are using a little trick here to only deselect lines already selected
Do lvMyList.$search(kFalse,kTrue,kTrue)

 

Looping through selected lines

;  Note that we can use lvMyList.$first(kTrue) but I find this saves me one line of code
Calculate lvMyList.$line as 0 ;; search from start
While lvMyList.$next(lvMyList.$line,kTrue)
; Do your thing
End While

 

Looping through lines that match a criteria without changing the selection of lines

;  Note that you need to make sure the current line does not change within the loop
; (or that you restore it if it does)
Calculate lvMyList.$line as 0 ;; search from start
While lvMyList.$search($ref.MyColumn='FindUs',kfalse,kfalse,kfalse,kfalse)>0
; Do your thing
End While

 

And finally a really nice routine that allows you to find the line in the list you are after. This routine is much faster then $search but has two limitations:
1) the column you are searching on must have a unique value
2) the list must be sorted on the column you are search on
Obviously the sort could negate any performance gain but if you have already retrieved the data sorted or if you do multiple searches on the list the overhead of the sort can become neglectable (note, I'm doing this as a table class method but alternatively you can just send the list as a field reference parameter and store it in an object or code class) :

tBaseClass.$quickSearch(char pvColumnName, fieldref pvValue, 
int pvFirstLine=1, int pvLastLine=$cinst.$linecount)
--
;; Searching algoritm based on the well known QuickSort and QuickSearch patterns
;; List must be sorted and column must be unique

If pvFirstLine>pvLastLine ;; Sorry, not in this list
Calculate $cinst.$line as 0
Quit Method 0
End If

If $cinst.[pvFirstLine].[pvColumnName]=pvValue
Calculate $cinst.$line as pvFirstLine
Quit Method pvFirstLine
Else If pvFirstLine=pvLastLine|$cinst.[pvFirstLine].[pvColumnName]>pvValue ;; Sorry, not in this list
Calculate $cinst.$line as 0
Quit Method 0
End If

If $cinst.[pvLastLine].[pvColumnName]=pvValue
Calculate $cinst.$line as pvLastLine
Quit Method pvLastLine
Else If pvFirstLine+1=pvLastLine|$cinst.[pvLastLine].[pvColumnName]<pvValue ;; Sorry, not in this list
Calculate $cinst.$line as 0
Quit Method 0
End If

; Find halfway point
Calculate lvHalfLine as pvFirstLine + ((pvLastLine-pvFirstLine)/2)
If lvHalfLine=pvFirstLine
Calculate lvHalfLine as lvHalfLine+1
Else if lvHalfLine=pvLastLine ;; Can this even happen?
Calculate lvHalfLine as lvHalfLine-1
End If

If $cinst.[lvHalfLine].[pvColumnName]=pvValue
Calculate $cinst.$line as lvHalfLine
Quit Method lvHalfLine
Else If $cinst.[lvHalfLine].[pvColumnName]>pvValue
; Look in the first half of the list
Quit Method $cinst.$quickSearch(pvColumnName,pvValue,pvFirstLine+1,lvHalfLine-1)
Else
; Look in the last half of the list
Quit Method $cinst.$quickSearch(pvColumnName,pvValue,lvHalfLine+1,pvLastLine-1)
End If
 

Now to use the quicksearch simply do a:

Calculate lvValue as 'MyUniqueValue'
Do lvMyList.$quickSearch('MyColumn',lvValue)

 

Add to: Digg | Technorati | del.icio.us | Stumbleupon | reddit | Furl

Leave a reply »

 

Leave a reply

Nickname
 
Email
 
Website
 
Confirmation image
 
Confirmation code
 
Comment