The reference page for lsearch can be found at http://www.purl.org/tcl/home/man/tcl8.5/TclCmd/lsearch.htm The simplest invocation form for lsearch is: lsearch list search_term lsearch searches the first argument list for an [element] that matches the search_term argument. [caspian]: lsearch returns '''-1''' if search_term was not found. [RS]: Note that in recent versions [lsearch] has gained many additional options and almost turned into a powerful "search engine": * '''-sorted''' requires the list to be sorted, and does faster binary search * '''-inline''' returns the element(s) found (or {} if nothing found, not -1!) * '''-all''' returns all matches (by index, or, with ''-inline'', by value) * '''-regexp''' matches with a regular expression, not a [glob] pattern as is the default * '''-exact''' matches exactly, not a [glob] or regexp pattern- useful for searches involving '*' So the following call has grep-like functionality, if you search a list of lines: lsearch -regexp -inline -all $lines $regexp A cute little wrapper for the most frequent use case (for me at least) is: proc in {list element} {expr [lsearch -exact $list $element] >= 0} [LWV] So, an example of using this wrapper would be: if {[in $yourlist SUSE]} { puts Found } [DKF]: Also, [Tcl 8.5] has the [in] operator. ---- Suppose you have this list: set yourlist {RedHat SUSE Debian Knoppix Peanut Mandrake Slackware} ...and you run '''lsearch''' to check if a given Linux distribution is present in the list. If it is, it will return the index, i.e. its position in the list. lsearch $yourlist RedHat ...would return '''0''' (zero) because the count starts from zero and "RedHat" is the first item in the list. "SUSE" would return '''1''' (one) and "Slackware" would return '''6''' (six). If the item is not present in the list, the search returns '''-1''' (minus one), so in order to check the presence of an item in the list, we can just check if the return value is a positive number, i.e. greater than zero or zero: if { [ lsearch $yourlist Debian ] >= 0 } { puts "Debian is in the list" } else { puts "There is no Debian in the list" } "RedHat" can be a tricky string to search because it is mixed case, so scroll down this page to see how we can run a case-insensitive search within a list. It involves using [regular expressions] in the search string. ([Matthias Hoffmann]: But wouldn't it be better to implement a '''-nocase'''-operator for those cases to avoid using the 'battleship' regexp for such simple daily cases...????) If you get to use regular expressions, '''-inline''' becomes a very useful switch. It makes '''lsearch''' return the element found instead of its index. Or, instead of regular expressions, we can use a simple [glob] pattern. Run... lsearch -inline $yourlist *ware ... and get '''Slackware''' instead of a relatively meaningless integer, i.e. the list index. But note that all searches above will return '''the first''' element found. If you want to find all matches, use the '''-all''' switch: lsearch -all $yourlist *an* ...returns the list '''{2 4 5}''': Debi'''an''', Pe'''an'''ut and M'''an'''drake. Combine '''-all''' with '''-inline''' and get all names instead of all indices: lsearch -all -inline $yourlist *an* ... returns the list {Debi'''an''' Pe'''an'''ut M'''an'''drake} ---- The default for lsearch is -glob . However, be certain the glob behavior is what you expect. For instance, check out this code. set a [list field1:val1=field3 field2:val2=2 field3:val3=3 field3] set b [lsearch $a field3] puts $b Would you expect glob to mean that element 0, 2, or 3 would be returned? 3 is the answer you get. On the other hand, set b [lsearch -regexp $a field3] puts $b returns 0. ---- '''Case-insensitive lsearch:''' Use (?i) in the -regexp for case-insensitive comparison: % lsearch -regexp {Foo Bar Grill} (?i)^BAR$ 1 ---- From Tcl 8.4, fancy new modes have been added: -all gives you all instances (instead of the first only); -inline gives the elements themselves instead of their index. So a very easy filter that removes empty sublists from a list is lsearch -all -inline $list ?* ;# RS - example: % lsearch -all -inline {foo {} bar {} grill} ?* foo bar grill ---- From 8.5 on there will be even more switches, for example TIP 127 [http://www.tcl.tk/cgi-bin/tct/tip/127] -index option. [JMN] 2005-12-11 Along with new commands like lrepeat.. this makes Tcl pretty neat for manipulating matrices ([nested list]s). e.g set m [lrepeat 3 [lrepeat 3 0]] % {0 0 0} {0 0 0} {0 0 0} lset m 1 0 1 ; lset m 2 0 2 % {0 0 0} {1 0 0} {2 0 0} Now you can retrieve in columnwise fashion like this: lsearch -all -inline -subindices -index 0 $m * %0 1 2 But it seems a bit funny to be using lsearch in 'glob' mode when we are really wanting to do positional access. Without resorting to extensions.. is there a better way? [schlenk] Simply lindex is enough: list [lindex $m 0 0] [lindex $m 1 0] [lindex 2 0] ---- [LV] I'm trying to figure out whether lseach can help search a nested list. set nl [list [list a 100% red] [list b 96.8 yellow] [list c 3.1415 green]] proc whereis {nlist srcterm} { return [lsearch -nocase $nlist $srcterm] } % whereis $nl yellow -1 That's not what I was wanting. What I would like to get is a 1 - yellow is in the second sublist of $nl in this case. I don't want to limit the search to one subelement of each sublist. Do I have to write a '''lsearchn''' which loops through all the nested lists and do a lsearch on each one? # Takes same arguments as lsearch, but treates each element # as if it were a single depth sublist to lsearch. # Limitations: should handle any depth # return value should be different depending on options, etc. proc lsearchn {args} { set argc [llength $args] set patt [lindex $args end] set list [lindex $args end-1] set options [lrange $args 0 end-2] set index 0 foreach sublist $list { set return [lsearch {expand}$options $sublist $patt] if {$return != -1} { break } incr index } if {$return eq -1} { set index -1 } return $index } There's probably a lot of error handling, etc. that needs to be done there. [MJ] - This was using strings as lists at several places without making sure they were actual valid lists (as argument to foreach and as argument to the lsearch). Added some splits to remedy this. proc lsearchn {args} { set argc [llength $args] set patt [lindex $args end] set list [split [lindex $args end-1]] set options [lrange $args 0 end-2] set index 0 foreach sublist $list { set sublist [split $sublist] set return [lsearch {expand}$options $sublist $patt] if {$return != -1} { break } incr index } if {$return eq -1} { set index -1 } return $index } [MJ] - Note that [split] is not always the intended way to create a list from the string, especially when the string contains multiple spaces in a row or newlines. If you have data like that, use the first version. However, you need to pass a valid list whose elements are also valid lists. If this is not the case an error will be thrown. If you have control over the data coming into this proc (e.g. you create the lists yourself) the best way is to make sure the list and its elements are valid lists. Note this is not how to do it: # e1 and e2 contain elements of the sublist # slx are the sublists set sl1 "{$e1 $e2}" set list "{$sl1 $sl2 sl3}" This is # $slXeX are elements of the sublist X set list [list [list $sl1e1 $sl1e2] [list $sl2e1 $sl2e2]] Summarizing if you intend to use something as a list, make sure that it is a valid list when creating or storing it, this will save you a lot of hassles later. ---- [LV] 2007 Dec 20 Here's the problem I'm working on. I've used [array get] to get a list consisting of a set of keys and values. Ideally, what I want to do is search only the values. Not only that, but I want to be able to search the values ignoring case. And, just to make things even more difficult, I need to be able to provide a substring of data in the entry. So, for example, let's say that we have: set contents [list PROJECT ABC123 LEADER JDOE DESCRIPTION [list SCREENSAVER SOFTWARE] RELEASE_COORD JSMITH] So, I need to be able to look for "abc" or "doe" and find a match here. I modified a version of the ''in'' proc to be: proc in {list element} {expr [lsearch -nocase -glob [append "*" ${list} "*"] $element] >= 0} set searchkey "abc" if {[in $contents "$::searchkey"]} { myprint $contents } but this doesn't ignore the key fields, and doesn't seem to do the substring thing right. Anyone have some ideas how to get closer to what I am wanting? --- I am trying to avoid a loop and wonder if there is a way: Suppose my list contains patterns and I would like to see if a particular instance occurs in that list. The -regexp option does not help since it does the matching the opposite way. Say: set l [list john* james* and*] lsearch $l "johnson" --> should match james* entry in the list lsearch $l "anderson" --> should match "and*" in the list and so on --- LV are you certain you don't mean that johnson should match john* ? --- [newp] Yes, you are right: it should match john*. I am just learning how to edit these wiki pages. ---- See also * [list] * [lappend] * [lindex] * [linsert] * [llength] * [lrange] * [lreplace] * [lsort] [Recursive list searching] gives you an index vector which can be used with [lindex]/[lset]. ---- I am trying to avoid a loop and wonder if there is a way: Suppose my list contains patterns and I would like to see if a particular instance occurs in that list. The -regexp option does not help since it does the matching the opposite way. Say: set l [list john* james* and*] lsearch $l "johnson" --> should match james* entry in the list lsearch $l "anderson" --> should match "and*" in the list and so on [LV] are you certain you don't mean that johnson should match '''john*''' ? ---- !!!!!! [Tcl syntax help] - [Arts and crafts of Tcl-Tk programming] %| [Category Command] |% !!!!!!