Reputation: 179
I have a collection of things, some of them empty.
I want to form a collection of non empty things, separated by separator.
this is essentially what I do in c++, but fails with any and all combinations of $ signs etc. etc.
I've already got a work around thanks, I'd like to know why and how this fails.
set q1 "a"
set q2 ""
set q3 "c"
set q4 d
set q5 ""
set answer ""
set needSeparator 0
foreach { var } {
q1 q2 q3 q4 q5
} {
if { $var ne "" } {
if {$needSeparator} {
append answer " separator "
}
append answer $var
set needSeparator 1
}
}
# expecting answer to be "a separator c separator d"
puts $answer
edit 2021-09-14
Following on from @Shawn
< if { $var ne "" } {
---
> if { [set elem [set $var]] ne "" } {
< append answer $var
---
> append answer $elem
on my effort does the job.
not quite sure how set
is doing the dereferencing there
but that's one for another day.
this was a minimal example so the rather more funky answers are too involved for someone trying to program in c++ :-). The qNs are horrible and come from different places, but the final code example is sweet and works translated back into my real problem - see below
# build compound SELECT
set q1 [select $mapText "final_text"]
set q2 [select $parish "parish"]
set q3 [select $la "local_authority"]
set q4 [sqSelect $five00]
set q5 ""
if {$nation ne "All"} {
set q5 {SELECT pin_id AS id FROM gazetteer WHERE nation = '}
append q5 $nation "'\n"
}
set compound {}
foreach clause {q1 q2 q3 q4 q5} {
if {[set q [set $clause]] ne ""} {
lappend compound $q
}
}
if {[llength compound] == 0} { return ""}
set res "WITH pinIds AS (\n"
append res [join $compound "INTERSECT\n "] ")\n"
Thanks for your help
Upvotes: 1
Views: 85
Reputation: 246877
Not an answer, but a response to the array for
comments on Shawn's answer
A Tcl implementation of array for
proc array_for {vars arrayName body} {
if {[llength $vars] != 2} {
error {array for: "vars" must be a 2 element list}
}
lassign $vars keyVar valueVar
# Using the complicated `upvar 1 $arrayName $arrayName` so that any
# error messages propagate up with the user's array name
upvar 1 $arrayName $arrayName \
$keyVar key \
$valueVar value
set sid [array startsearch $arrayName]
# If the array is modified while a search is ongoing, the searchID will
# be invalidated: wrap the commands that use $sid in a try block.
try {
while {[array anymore $arrayName $sid]} {
set key [array nextelement $arrayName $sid]
set value [set "${arrayName}($key)"]
uplevel 1 $body
}
} trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
puts stderr [list $e]
dict set e -errorinfo "detected attempt to add/delete array keys while iterating"
return -options $e
} finally {
array donesearch $arrayName $sid
}
return
}
and add to the array
ensemble:
set map [namespace ensemble configure array -map]
dict set map for ::array_for
namespace ensemble configure array -map $map
Given that, an array values
subcommand can be easily created (to pair with array names
)
proc array_values {arrayName} {
upvar 1 $arrayName ary
set values [list]
array for {name value} ary {lappend values $value}
return $values
}
Upvotes: 0
Reputation: 52449
You're better off using a list, dict or array to store related values instead of a bunch of different variables. But any way your data is stored, lappend
the non-empty values to a list or otherwise filter out the empty ones and join
the result:
#!/usr/bin/env tclsh
set data {a "" c d ""}
# Using foreach
set answer {}
foreach elem $data {
if {$elem ne ""} {
lappend answer $elem
}
}
puts [join $answer " separator "]
# Using lmap for a more functional style; note eq instead of of ne
set answer [lmap elem $data { if {$elem eq ""} continue; set elem }]
puts [join $answer " separator "]
# Using a dict
set data [dict create q1 a q2 "" q3 c q4 d q5 ""]
set answer {}
# Dict traversal happens in the same order keys were added
dict for {_ elem} $data {
if {$elem ne ""} {
lappend answer $elem
}
}
puts [join $answer " separator "]
When iterating through a list of variable names, you have to use set
to get the value of the current name (In your code, $var
is q1
, q2
, etc. which are always going to be not equal to an empty string):
set answer {}
foreach varname {q1 q2 q3 q4 q5} {
set elem [set $varname]
if {$elem ne ""} {
lappend answer $elem
}
}
puts [join $answer " separator "]
Upvotes: 3