How to remove items from a list not by their position ([lreplace]) but by the actual content.
My solution would be:
======
proc K { x y } { set x }
proc lremove { listvar string } {
upvar $listvar in
foreach item [K $in [set in [list]]] {
if {[string equal $item $string]} { continue }
lappend in $item
}
}
======
which gives us
% set a [list a b c a b c a b c]
a b c a b c a b c
% lremove a b
% set a
a c a c a c
Is there any reason not to do it that way?
Gotisch
----
[RS] proposes this built-in way, waving many flags :)
======
% set a [lsearch -all -inline -not -exact $a b]
a c a c a c
======
[wdb] Simply ingenious!
----
[@JaviGarate] Awesome!
----
Todd A. Jacobs suggests this slightly more flexible version:
======
# lremove ?-all? list pattern
#
# Removes matching elements from a list, and returns a new list.
proc lremove {args} {
if {[llength $args] < 2} {
puts stderr {Wrong # args: should be "lremove ?-all? list pattern"}
}
set list [lindex $args end-1]
set elements [lindex $args end]
if [string match -all [lindex $args 0]] {
foreach element $elements {
set list [lsearch -all -inline -not -exact $list $element]
}
} else {
# Using lreplace to truncate the list saves having to calculate
# ranges or offsets from the indexed element. The trimming is
# necessary in cases where the first or last element is the
# indexed element.
foreach element $elements {
set idx [lsearch $list $element]
set list [string trim \
"[lreplace $list $idx end] [lreplace $list 0 $idx]"]
}
}
return $list
}
# Test cases.
set foo {1 2 3 4 5 6 7 8 9 10}
puts "Foo: [list $foo]"
puts "Foo: replace one: [lremove $foo $argv]"
puts "Foo: replace all: [lremove -all $foo $argv]"
puts {}
set bar {1 2 3 4 1 6 7 1 9 10}
puts "Bar: [list $bar]"
puts "Bar: replace one: [lremove $bar $argv]"
puts "Bar: replace all: [lremove -all $bar $argv]"
======
[dzach] ...but then, when I do :
======
set bar {1 2 3 {} 4 1 6 7 1 9 10 {}}
lremove -all $bar {}
======
I still get:
1 2 3 {} 4 1 6 7 1 9 10 {}
[jmn] this is because the proc expects a *list* of patterns as the 2nd argument.
Try instead:
======
lremove -all $bar [list {}]
======
I don't see why the complicated 'lreplace' line with the 'string trim' is required.
As far as I can see - it could be replaced with:
======
set list [lreplace $list $idx $idx]
======
When removing a single value (1st encountered match) from a list - I use:
======
set posn [lsearch -exact $list $val]
set list [lreplace $list $posn $posn]
======
This is slightly different in functionality from RS's beautiful one liner above, which removes all instances.
If you know you only have one instance of the value to be removed - this 2 liner may be slightly faster on large lists (tested on Tcl8.6b1).. but on a 2007 vintage machine we're talking maybe a couple of hundred microseconds for lists even of size approx 10K, so it's not likely to be an issue except for some pretty intensive inner loops with large lists.
[milarepa] How about this:
======
proc lremove {list match} {
set idx_list [lsearch -all $list $match]
foreach idx [lreverse $idx_list] {
set list [lreplace $list $idx $idx]
}
return $list
}
======
[gold] Multiple entries, using Suchenworth's idea:
======
# written on Windows XP on eTCL
# working under TCL version 8.5.6 and eTCL 1.0.1
# gold on TCL WIKI , 3jul2013
package require Tk
proc cleaner {target args} {
set res $target
foreach unwant $args {
# suchenworth idea
set res [lsearch -all -inline -not -exact $res $unwant ]
}
return $res
}
console show
set a_target_list [list a b c a b c a b c 1234 1234 5678]
puts " [cleaner {a b c a b c a b c} b c]"
puts " [cleaner $a_target_list 1234 5678]"
Output:
a a a
a b c a b c a b c
======
OAnother `lremove` methode, by http://wiki.tcl.tk/55526%|%Cyril %|% :
======
proc lremove {l p} {
set p [lsearch -exact $l $p]
return "[lrange $l 0 $p-1] [lrange $l $p+1 end]"
}
======
Example :
set l {a b c d e}
puts [lremove $l c]
Output:
a b d e
======set l {a b c d e}
puts [lremove $l c]
# => a b d e
======
2018-09-13: note that `[concat [lrange $l 0 $p-1] [lrange $l $p+1 end]]` is a better way to concatenate the lists.
----
See also
* [list]
* [http://tip.tcl.tk/367.html%|%TIP #367]
<<categories>> Command