Nested lists in \foreach loop in TikZ
\foreach
strips spaces before each list item, and then collects everything up to the next comma (or the list terminating token) before assigning to the relevant variables.
The \foreach
problem arises in this case because of the extra space given before the closing brace in the list. Consider the following:
\foreach \p/\q in {a/{1,2,3}, b/{4,5,6} }
\foreach \r in \q { (\r) }
This produces:
(1) (2) (3) (4,5,6 )
the same effect is found if there is a space before the comma in the first list item:
`\foreach \p/\q in {a/{1,2,3} , b/{4,5,6} }
\foreach \r in \q { (\r) }
which produces
(1,2,3 ) (4,5,6 )
Note, this effect will also appear if there is a space after the /
when using multiple assignment.
\foreach \p/\q in {a/ {1,2,3},b/{4,5,6}}
\foreach \r in \q { (\p:\r) }
which produces
(1,2,3 ) (4) (5) (6)
By removing these spaces the problems disappear...
\foreach \p/\q in {a/{1,2,3}, b/{4,5,6}}
\foreach \r in \q { (\r) }
which produces
(1) (2) (3) (4) (5) (6)
Should all spaces be stripped automatically? Automatically stripping spaces at the beginning is trivial (\pgfutil@ifnextchar x{...}{...}
is used all over the place in PGF parsing), however it is a relatively expensive operation.
Stripping space after items is hard and would involve character by character parsing, which would be very slow. So, it is easier (and not that inconvenient) just to be careful about spaces.
The other problem is the coordinate specification. Anything that isn't obviously one of the known coordinate systems is assumed to be a node, so 00
, 14
are treated as node names. There are many ways to split this specification up, here is one using the .expanded
key handler:
\documentclass[border=0.125cm]{standalone}
\usepackage{tikz}
\tikzset{
at xy split/.style 2 args={
at={(#1,#2)}
},
a/.style={circle, draw=red},
b/.style={rectangle, draw=blue}
}
\begin{document}
\begin{tikzpicture}
\foreach \n/\lst in {a/{00, 14, 20, 34}, b/{01, 11, 23, 33}} {
\foreach \xy in \lst {
\node[\n/.try, at xy split/.expanded=\xy] {\n};
}
}
\end{tikzpicture}
\end{document}
Here is a very simple solution this advice from pgfmanual: "Different list items are separated by commas. However, this causes a problem when the list items contain commas themselves as pairs like (0,1) do. In this case, you should put the items containing commas in braces as in {(0,1)}."
\documentclass{article}
\usepackage{pgffor}
\begin{document}
\foreach \n/\lst in {%
{a/00, 14, 20, 34},%
{b/01, 11, 23, 33},%
{c/02, 12, 26, 36}%
} {
\foreach \xy in \lst {
(\n,\xy)
}
}
\end{document}
Another example with coordinates:
\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\foreach \n/\lst in {%
{a/(0,0),(1,1)},%
{b/(1,0),(0,1)},%
{c/(2,2)}%
} {
\foreach \xy in \lst {
\node[circle,draw,minimum size=8mm] at \xy {\n};
}
}
\end{tikzpicture}
\end{document}
edit (2017): since xint 1.1 (2014/10/28)
one needs here \usepackage{xinttools}
, not \usepackage{xint}
. Code updated.
(I am editing this to remove comments made obsolete by release 1.09f
of xint
)
TikZ code will surely arrive here quickly. In the mean time, you can do this kind of things with the \xintFor
loops from xint
\documentclass{article}
\usepackage{xinttools}
\usepackage{tikz}
\begin{document}
% starting point, this code from OP
% \foreach \n/\lst in { a/{00, 14, 20, 34}, b/{01, 11, 23, 33} } { \foreach \xy
% in \lst { \node[\n] at (\xy) {\n}; } }
% defining a node named xy at position (x,y)
%
\def\DefineNode #1{\definenode #1}
\def\definenode #1#2{(#1,#2) node (#1#2) [shape=circle,draw] {#1#2}}
\begin{tikzpicture}
% FIRST, I need to name a bunch of nodes in an automated way,
% I decide that xy is at coordinates (x,y).
%
% This code applies \DefineNode to each item, but it appears that this must
% be done before the \path command, hence the \expandafter's.
% (the \xintApplyUnbraced needs two expansions to get fully expanded)
%
\expandafter\expandafter\expandafter\path
\xintApplyUnbraced\DefineNode {\xintCSVtoList{00, 14, 20, 34, 01, 11, 23, 33}}
;
% SECOND, I do what was asked in the OP
% xint 1.09f allows spaces in the input, around commas and parentheses
%
\xintForpair #1#2 in
{ (color=blue, { 00 , 14 , 20 , 34 } ),
(color=red, { 01 , 11 , 23 , 33 } ) }
\do
{%
\xintFor #3 in {#2} \do {\node [#1] at (#3) {#3}; }%
}
\end{tikzpicture}
\end{document}