6

I try to use nested \foreach loops to create edges between many nodes.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \node (a) at (0, 1) {};
  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};
  \node (d) at (-1, 0) {};
  \foreach \from/\targets in {%
    b/d,%
    {a/b,c,d}%
  } {
    \typeout{\from-\targets}
    \foreach \to in {\targets} {
      \typeout{ \from-\to}
      \path (\from) edge (\to);
    }
  }
\end{tikzpicture}
\end{document}

But this fails with the cryptic error

b-d
 b-d
a-b,c,d
 a-b,c,d

! Package PGF Math Error: Unknown function `b' (in 'b').

As \typeout visible before the error show, seemingly \targets is not expanded and thus interpreted a single parameter instead of a list. Can expansion be forced at this point? Or is there another way to fix it?

jub0bs
  • 58,916
XZS
  • 2,953

3 Answers3

4

You need to remove the parentheses from the inner loop. Namely, instead of passing {\targets} to the \foreach loop, you should be passing \targets.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}
  \node (a) at (0, 1) {};
  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};
  \node (d) at (-1, 0) {};
  \foreach \from/\targets in {%
    b/d,%
    {a/b,c,d}%
  } {
    \typeout{\from-\targets}
    \foreach \to in \targets {
      \typeout{ \from-\to}
      \path (\from) edge (\to);
    }
  }
\end{tikzpicture}

\end{document}

enter image description here

A.Ellett
  • 50,533
4

Problems with \foreach aside, I think the syntax

\path (b) edge (d)
      (a) edge (b) edge (c) edge (d);

comes much more natural. It is also much easier to change edge options on an edge-to-edge basis (or for more than one edge, too):

\path (b) edge (d) {[thick]
      (a) edge (b) edge[green] (c) edge[red,->] (d)};

Though, a similar thing can still be done with \foreach, say

\path (b) edge (d) {[thick]
      (a) \foreach \opt/\p in {/b, green/c, {red,->}/d}
                  {edge[style/.expand once=\opt] (\p)}};

With my qrr.misc library (code similar to the keys in another answer of mine), one can do

\path (b) edge (d) {[thick]
      (a) [edges to={b,[green] c, [{red,->}] d}]};

It should be noted that the , in the options has to be protected somehow, either in that way shown or by doing {[red, ->] d}, i.e. enclosing the whole thing in braces. (This could be made more robust or can be changed to use ( and ) around the coordinates instead of using ,, but the implementation will need more parsing then.)

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{qrr.misc}
\begin{document}
\begin{tikzpicture}
  \node (a) at (0, 1) {};  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};  \node (d) at (-1, 0) {};
  \path (b) edge (d) {[thick]
        (a) edge (b) edge[green] (c) edge[red,->] (d)};
\end{tikzpicture}
\begin{tikzpicture}
  \node (a) at (0, 1) {};  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};  \node (d) at (-1, 0) {};
  \path (b) edge (d) {[thick]
        (a) \foreach \opt/\p in {/b, green/c, {red,->}/d}
                    {edge[style/.expand once=\opt] (\p)}};
\end{tikzpicture}
\begin{tikzpicture}
  \node (a) at (0, 1) {};  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};  \node (d) at (-1, 0) {};
  \path (b) edge (d) {[thick]
        (a) [edges to={b,[green] c, [{red,->}] d}]};
\end{tikzpicture}
\end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
2

The important thing is to be consistent in the way you specify elements in a list to be processed by \foreach.

In the approach below, which I find more intuitive, \targets gets expanded to {b} at the first iteration of the outer loop, and to {b,c,d} at the second one. Therefore, specifying \foreach \to in \targets for the inner loop works as expected.

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \node (a) at (0, 1) {};
  \node (b) at (0, -1) {};
  \node (c) at (1, 0) {};
  \node (d) at (-1, 0) {};
  \foreach \from/\targets in {b/{d},a/{b,c,d}}
  {%
    \typeout{\from-\targets}
    \foreach \to in \targets
    {%
        \typeout{ \from-\to}
      \path (\from) edge (\to);
    }
  }
\end{tikzpicture}
\end{document}
jub0bs
  • 58,916