简体   繁体   中英

Tcl: How can I search a nested list in Tcl 8.4?

I have a nested list as follows:

set myList {
   {first apples dollars}
   {second bananas euros}
   {third pears pesos}
}

In Tcl 8.7, I can use lsearch -index 2 (eg) if I want to find the type of fruit I'm selling in euros:

set myFruit [lindex $myList [lsearch -index 2 $myList "euros"] 1]
puts $myFruit    
# returns bananas

But how do I do that in Tcl 8.4 where the -index switch doesn't exist? Is there a shortcut, or will I have to use a foreach ?

I would like to do this with TCl's native functionality, if possible, and not via any additional packages (which I unfortunately cannot use for my particular project).

Using the wildcard option of lsearch might be what you are looking for?

set myList {
   {first apples dollars}
   {second bananas euros}
   {third pears pesos}
}

set myFruit [lindex $myList [lsearch $myList "*euros*"] 1]
puts $myFruit

Though the above would also match the word 'neuroscience' for example, so you could use something like the below if that's a possibility:

lsearch -regexp $myList {\yeuros\y}

If it gets more complex, like, if you want to match the entire sub-element (and therefore not want to match euros in the element {forth kiwi "0.5 euros each"} then instead of trying to make the search more flexible, I believe it would be much easier to use a loop:

for {set i 0} {$i < [llength $myList]} {incr i} {
    if {"euros" in [split [lindex $myList $i]]} {
        set myFruit [lindex $myList $i]
        puts $myFruit
    }
}

This is more an extension to Jerry's answer:

If going down the regexp route, and you can guarantee that the input list of lists is regular in the sense of a sanitized Tcl-list string representation, then you can have regexp do all the work:

 lindex [regexp -all -inline {([^\s]+)\s+euros\}} $myList] 1

This also covers for the case of multiple sub-lists having euros as elements.

 foreach {_ v}  [regexp -all -inline {([^\s]+)\s+euros\}} $myList] {puts $v}

According to 589332 :

package require Tclx

set somelist {{aaa 1} {bbb 2} {ccc 1}}
set searchTerm "bbb"
if {[keylget somelist $searchTerm myvar]} {
    puts "Found instance of $searchTerm, value is: $myvar"
}

The keylget command in this syntax returns 1 if found, 0 if not. The value next to bbb (2) is then placed in the variable myvar. Note that there should be no dollar sign ($) in front of somelist in the keylget command.

So for your example that would be:

set searchTerm "euros"
if {[keylget myList $searchTerm myFruit]} {
    puts $myFruit
}

(Not marking this as a duplicate because 589332 didn't deal with v8.4 specifically; someone just happened to mention it in one of the answers.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM