Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.
\documentclass{article}
\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
 \foreach \x/\y in {\firstlist/\secondlist} {
    \draw(0,\x)--(1,\y);
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

How can I make the output of the \testa command equal to the output of the \testb command?

share|improve this question
    
Welcome to TeX.sx! This is a nice minimal example. I think you want to have another picture with \testb in it though, right? –  Alan Munn Jul 29 at 22:12
    
@AlanMunn yes, to see the difference between the outputs. \testb is the desired output. –  Carlitos Jul 29 at 22:14

4 Answers 4

up vote 10 down vote accepted

Here is a very simple solution:

\documentclass{article}
\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
  \foreach \x [count=\c,evaluate=\c as \y using {{\secondlist}[\c-1]}]  in \firstlist {
    \draw(0,\x)--(1,\y);
  }
}

\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}

An extended solution defining the new style parallel foreach. You can use multiple parallel lists:

\documentclass{article}
\usepackage{tikz}

\pgfset{
  foreach/parallel foreach/.style args={#1in#2via#3}{evaluate=#3 as #1 using {{#2}[#3-1]}},
}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}
\def\thirdlist{1,2,0}

\newcommand{\testa}{
  \foreach \x [count=\c,
  parallel foreach=\y in \secondlist via \c,
  parallel foreach=\z in \thirdlist via \c]
  in \firstlist
  {
    \node[left] at (0,\x) {\x};
    \draw(0,\x)--(1,\y);
    \draw[red](0,\x)--(1,\z);
  }
}

\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\end{document}
share|improve this answer

You can count how many elements there are in one of the arrays and use that number as the foreach limit. Then every spin accesses one element of the arrays;

\documentclass[tikz]{standalone}
\def\firstlist{{0,1,2}} % <== Notice the double brace for array notation
\def\secondlist{{0,1,2}}

\newcommand{\testa}{
\pgfmathdim{\firstlist}%Get the number of elements in array
  \foreach \x in {0,...,\numexpr\pgfmathresult-1\relax}{%array index starts from zero
    \draw(0,{array(\firstlist,\x)})--(1,{array(\secondlist,\x)});
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\begin{scope}[shift={(2,0)}] %Test the result with \testb next to it
\testb
\end{scope}
\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer

I'm not so sure you want to do it. ;-)

\documentclass{article}
\usepackage{xparse}

\def\xforeach#1#{\xforeachaux{#1}}

\ExplSyntaxOn
\NewDocumentCommand{\xforeachaux}{mm}
 {
  \carlitos_xforeach:nn { #1 } { #2 }
 }

\seq_new:N \l_carlitos_xf_first_seq
\seq_new:N \l_carlitos_xf_second_seq
\seq_new:N \l_carlitos_xf_list_seq

\cs_new_protected:Npn \carlitos_xforeach:nn #1 #2
 {
  \seq_clear:N \l_carlitos_xf_list_seq
  \__carlitos_split:n { #2 }
  \seq_mapthread_function:NNN
   \l_carlitos_xf_first_seq
   \l_carlitos_xf_second_seq
   \__carlitos_xf_additem:nn
  \__carlitos_xf_do:nx { #1 } { \seq_use:Nn \l_carlitos_xf_list_seq { , } }
 }
\cs_new_protected:Npn \__carlitos_split:n #1
 {
  \__carlitos_split_aux:w #1 \q_stop
 }
\cs_new_protected:Npn \__carlitos_split_aux:w #1/#2 \q_stop
 {
  \seq_set_split:Nno \l_carlitos_xf_first_seq { , } { #1 }
  \seq_set_split:Nno \l_carlitos_xf_second_seq { , } { #2 }
 }
\cs_new_protected:Npn \__carlitos_xf_additem:nn #1 #2
 {
  \seq_put_right:Nn \l_carlitos_xf_list_seq { #1/#2 }
 }
\cs_new_protected:Npn \__carlitos_xf_do:nn #1 #2
 {
  \foreach #1 { #2 }
 }
\cs_generate_variant:Nn \seq_set_split:Nnn { Nno }
\cs_generate_variant:Nn \__carlitos_xf_do:nn { nx }
\ExplSyntaxOff

\usepackage{tikz}

\def\firstlist{0,1,2}
\def\secondlist{0,1,2}

\newcommand{\testa}{
 \xforeach \x/\y in {\firstlist/\secondlist} {
    \draw(0,\x)--(1,\y);
    }
}

\newcommand{\testb}{
 \foreach \x/\y in {0/0,1/1,2/2} {
    \draw(0,\x)--(1,\y);
    }
}
\begin{document}
\begin{tikzpicture}
\testa
\end{tikzpicture}
\qquad
\begin{tikzpicture}
\testb
\end{tikzpicture}
\end{document}

enter image description here


A completely different implementation, where you don't even need \x/\y; it supports up to three slash separated arguments, which can be either comma separated lists (spaces are ignored before and after the commas) or macros expanding to comma separated lists.

Each cycle in the foreach loop is performed using the second argument, which uses #1, #2 and #3 to denote the current list element. The number of cycles is determined by the first list.

Probably a better error recovery should be provided if the given items are more than three, but this is left as an exercise.

\documentclass{article}
\usepackage{xparse,tikz}

\ExplSyntaxOn

\NewDocumentCommand{\Xforeach}{mm}
 {
  \carlitos_Xforeach:nn { #1 } { #2 }
 }

\seq_new:N \l__carlitos_Xf_input_seq
\seq_new:N \l__carlitos_Xf_output_seq
\seq_new:N \l__carlitos_Xf_lista_seq
\seq_new:N \l__carlitos_Xf_listb_seq
\seq_new:N \l__carlitos_Xf_listc_seq
\tl_new:N \l__carlitos_Xf_tmp_tl
\int_new:N \l__carlitos_Xf_cycle_int

\cs_new_protected:Npn \carlitos_Xforeach:nn #1 #2
 {
  \seq_set_split:Nnn \l__carlitos_Xf_input_seq { / } { #1 }
  \seq_clear:N \l__carlitos_Xf_output_seq
  \seq_map_inline:Nn \l__carlitos_Xf_input_seq
   {
    \seq_put_right:No \l__carlitos_Xf_output_seq { ##1 }
   }
  \int_case:nnF { \seq_count:N \l__carlitos_Xf_input_seq }
   {
    { 1 } { \__carlitos_Xf_one:n   { #2 } }
    { 2 } { \__carlitos_Xf_two:n   { #2 } }
    { 3 } { \__carlitos_Xf_three:n { #2 } }
   }
   {
    Only~one,~two~or~three!
   }
 }

\cs_new_protected:Npn \__carlitos_Xf_one:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:n ##1 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:n { x }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:x { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
   }
 }
\cs_new_protected:Npn \__carlitos_Xf_two:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_second_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:nn ##1 ##2 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:nn { xx }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:xx
     { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_second_seq { \l__carlitos_Xf_cycle_int } }
   }
 }
\cs_new_protected:Npn \__carlitos_Xf_three:n #1
 {
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_first_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_second_seq { , } \l__carlitos_Xf_tmp_tl
  \seq_pop_left:NN \l__carlitos_Xf_output_seq \l__carlitos_Xf_tmp_tl
  \seq_set_split:NnV \l__carlitos_Xf_third_seq { , } \l__carlitos_Xf_tmp_tl
  \cs_set_protected:Npn \__carlitos_Xf_loop:nnn ##1 ##2 ##3 { #1 }
  \cs_generate_variant:Nn \__carlitos_Xf_loop:nnn { xxx }
  \int_zero:N \l__carlitos_Xf_cycle_int
  \seq_map_inline:Nn \l__carlitos_Xf_first_seq
   {
    \int_incr:N \l__carlitos_Xf_cycle_int
    \__carlitos_Xf_loop:xxx
     { \seq_item:Nn \l__carlitos_Xf_first_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_second_seq { \l__carlitos_Xf_cycle_int } }
     { \seq_item:Nn \l__carlitos_Xf_third_seq { \l__carlitos_Xf_cycle_int } }
   }
 }

\ExplSyntaxOff
\begin{document}
\def\firstlist{0,1,2}
\def\secondlist{0,1,2}
\def\thirdlist{a,b,c}

\Xforeach{\firstlist}{--#1-- }

\bigskip

\begin{tikzpicture}
\Xforeach{\firstlist/\secondlist}{\draw(0,#1)--(1,#2);}
\end{tikzpicture}\qquad
\begin{tikzpicture}
\Xforeach{0,1,2/\secondlist}{\draw(0,#1)--(1,#2);}
\end{tikzpicture}

\bigskip

\Xforeach{\firstlist/\secondlist/\thirdlist}{#1/#2/#3 }

\Xforeach{\firstlist/\secondlist/\thirdlist}{#1/#2/#3 }

\Xforeach{\firstlist/0,1,2/a,b,c}{#1/#2/#3 }
\end{document}

enter image description here

share|improve this answer
    
you managed to scare me :) –  Carlitos Jul 30 at 9:01
    
@Carlitos It just shows that keeping your proposed syntax is not really easy. I tried thinking to a better syntax, but it was too late in the night. Stay focused. –  egreg Jul 30 at 9:04
    
Well, in the end I discarded the list thing and opted for a completely different approach, since my real world problem was actually more complicated than that in the MWE. –  Carlitos Jul 30 at 9:09

You could always "roll your own" solution, but it would require considerable work to make it look as nice as \foreach:

\documentclass[tikz,border=5]{standalone}
\makeatletter
\def\attxt{@}
\long\def\frch#1#2#3{\long\def\frchaction##1##2{#3}%
\def\lsta{}\def\lstb{}%
\edef\lst{#1,@,/#2,@,}\expandafter\@frch\lst\@}
\def\@frch#1,#2/#3,#4\@{%
\def\tmpa{#1}\def\tmpb{#3}%
\ifx\tmpa\attxt%
  \ifx\tmpb\attxt%
  \else%
    \def\lstb{#3}\frchaction{\lsta}{#3}\@frch @,/#4,\@%
  \fi%
\else%
  \ifx\tmpb\attxt%
    \def\lsta{#1}\frchaction{#1}{\lstb}\@frch#2/@,\@%
  \else%
    \def\lsta{#1}\def\lstb{#3}\frchaction{#1}{#3}\@frch#2/#4\@%
  \fi%
\fi}

\begin{document}
\begin{tikzpicture}
\frch{0,1,2}{0,1,2}{ \draw (0,#1) -- (1,#2); }
\frch{0,1,2}{0,1,2,3,4,5}{ \draw (2,#1) -- (3,#2); }
\frch{0,1,2,3,4,5}{0,1,2}{ \draw (4,#1) -- (5,#2); }
\end{tikzpicture}
\end{document}

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.