I have N lists, e.g. 2 lists like these: {a {1 2 3}}
and {b {4 5 6}}
.
I would like to remap the values to obtain this list:
{{{a 1} {b 4}} {{a 2} {b 5}} {{a 3} {b 6}}}
What would be a good algorithm to do the above by starting from 2 or more lists and if the number of the second part of each list can be variable, i.e. {{a {1 2 3 4 5 .. n}}
?
I'm using Tcl 8.5 (which I can't upgrade).
I've no idea why you'd want that transform, but as far as I understand what you want this will do it:
proc weirdshuffle lists {
set tags {}
set iterators {}
foreach l $lists {
lassign $l tag vals
lappend tags $tag
lappend iterators $tag $vals
}
lmap {*}$iterators {
lmap tag $tags {
list $tag [set $tag]
}
}
}
proc weirdshuffle8.5 lists {
set tags {}
set iterators {}
foreach l $lists {
lassign $l tag vals
lappend tags $tag
lappend iterators $tag $vals
}
set res {}
foreach {*}$iterators {
set tmp {}
foreach tag $tags {
lappend tmp [list $tag [set $tag]]
}
lappend res $tmp
}
set res
}
puts [weirdshuffle8.5 {
{a {1 2 3}}
{b {4 5 6}}
{c {7 8 9}}
}]
It's easier to follow what it's doing with the lmap
version, but that command first appeared in Tcl 8.6, so weirdshuffle8.5
is an implementation that handles the loop accumulators explicitly.
The core idea is to exploit the fact that foreach
(and lmap
) support multiple iterators over different value lists, and construct the list of iterators and lists, then run the lmap
(or foreach
) on those.
For the paranoid, it's important to realise that doing this will create (or overwrite) variables in weirdshuffle
based on the values of the lists passed in, possibly breaking things if one of those clashes with the variables that proc uses. At the cost of some visual clutter, that could be defended against by making them indexes in arrays:
proc weirdshuffle_safe lists {
set tags {}
set iterators {}
foreach l $lists {
lassign $l tag vals
lappend tags t($tag) $tag
lappend iterators t($tag) $vals
}
lmap {*}$iterators {
lmap {tag name} $tags {
list $name [set $tag]
}
}
}
This also defends against the more pathological cases where the iterator variables look like fully qualified variables in other namespaces:
{::foo::bar {1 2 2}} {::global {4 5 6}}
which would otherwise be able to set arbitrary state outside of the proc callframe. If you're not in control of those tag values (a
and b
in your example), then I'd use the _safe
variant to be sure.