The answers I gave were in a discussion back in october of 1995. I made two postings to a discussion back then, one of which was to organize and collect examples from several other postings done by others: I can't lay claim to much originality here. The answers are mostly tcl-version-independent; I'm unaware of any special problem that might come up in 7.4. The two postings from 1995 are appended below. The examples should work in tcl 7.4 (though I didn't recheck in detail). -- Wayne Throop throopw@sheol.org http://sheol.org/throopw -- Path: aur.alcatel.com!usenet From: throopw%sheol.uucp@dg-rtp.dg.com (Wayne Throop) Newsgroups: comp.lang.tcl Subject: Re: unique elements of a list Date: 17 Oct 1995 15:37:47 GMT Organization: Alcatel Network Systems (Raleigh, NC) Lines: 23 Message-ID: <460ikb$ioo@aurns1.aur.alcatel.com> References: <1995Oct16.112919.417@rmcs.cranfield.ac.uk> NNTP-Posting-Host: aursag.aur.alcatel.com : From: sastry@rmcs.cranfield.ac.uk : I am looking for a way of obtaining unique elements of a list. : For example, given a list : %set a {apples pears peaches apples sapota oranges pears} : -a- tcl command would return the list (using lunique $a, for example) : %apples pears peaches sapota oranges set a {apples pears peaches apples sapota oranges pears} set b [foreach x $a {set u($x) 1};array names u] This produces the elements in randomish order... if you want stability, it's a bit tougher. If you want sorted, just set b [lsort [foreach x $a {set u($x) 1};array names u]] And of course, one could always use a cannon, and do set b [split [exec sort -u << [join $a \n]\n] \n] .... though it depends on the list elements not containing \n, I thinks. -- Wayne Throop throopw%sheol.uucp@dg-rtp.dg.com throop@aur.alcatel.com -- Path: aur.alcatel.com!usenet From: throopw%sheol.uucp@dg-rtp.dg.com (Wayne Throop) Newsgroups: comp.lang.tcl Subject: Re: unique elements of a list Date: 18 Oct 1995 16:18:49 GMT Organization: Alcatel Network Systems (Raleigh, NC) Lines: 89 Message-ID: <4639d9$is3@aurns1.aur.alcatel.com> References: <1995Oct16.112919.417@rmcs.cranfield.ac.uk> <45u2r1$h65@news.iwl.net> <460amp$ltb@paperboy.osf.org> NNTP-Posting-Host: aursag.aur.alcatel.com : From: loverso@osf.org (John Robert LoVerso) : Below is my "lunique2" proc which beats both the original posters code : [which is O(n^2)] and the "lrmdups" proc of tclX: : lunique: 206984 microseconds per iteration : lrmdups: 15200 microseconds per iteration : lunique: 7699 microseconds per iteration Interesting. Tinkering with the inner loop yields a further tiny increment. Perhaps surprisingly, doing the inner loop twice is a bit better than doing both inputs and outputs in one loop, presumably because it eliminates the need to probe for array element existance. See results appended. Note that the best of the "lunique" implementations below is 1800 or so, but time {foreach x $a {set u($x) 1};array names u} 100 yields less than 500. So, if the order of the output isn't significant, then just giving the code inline seems attractive, if performance is important. I'm not sure why lrmdups should perform so badly if it also sorts; I didn't play with TclX. -- Wayne Throop throopw%sheol.uucp@dg-rtp.dg.com throop@aur.alcatel.com -- # code of original poster proc lunique { list } { set newList "" for {set i 0} { $i <= [llength $list] } { incr i } { set elt [lindex $list $i] for {set j 0} { $j <= [llength $newList] } { incr j } { set eltNew [lindex $newList $j] if { [lsearch -exact $newList $elt] < 0 } { # append the element to newList lappend newList $elt } } } return $newList } # John Robert LoVerso's improved version proc lunique2 list { set result {} foreach e $list { if [info exists elems($e)] continue set elems($e) "" lappend result $e } return $result } # let's try squeezing characters out, and eliminate the "info" and "continue" proc lunique3 l { set r {} foreach e $l {set u($e) 1} foreach e $l {if $u($e) {lappend r $e;set u($e) 0}} return $r } # see whether skipping the set and lappend with continue is worthwhile proc lunique4 l { set r {} foreach e $l {if [info exists u($e)] continue;set u($e) {};lappend r $e} return $r } # see if "catch" does any better than "info" in existance probe proc lunique5 l { set r {} foreach e $l {if [catch {set u($e)}] {set u($e) 1;lappend r $e}} return $r } # all right, time 'em set a {apples pears peaches apples sapota oranges pears} foreach x {{} 2 3 4 5} { append r "$x [time [list lunique$x $a] 100]\n" } append x $r 24376 microseconds per iteration 2 2006 microseconds per iteration 3 1813 microseconds per iteration 4 1844 microseconds per iteration 5 2766 microseconds per iteration