5

Suppose that two lists of identical length have been defined with e.g., etoolbox. Is it possible to have a function \do taking two parameters that will process both lists simultaneously, taking one parameter from each list?

For example, suppose one list contains text and the second, URL, would it be possible to have a \do#1#2 function that would return \hyperref[#1]{#2} go through both lists simultaneously?

I am thinking of etoolbox's dolistloop but with two lists given as parameters instead of just one.

A small (necessarily incomplete) MWE

\documentclass{article}
\usepackage{etoolbox}

\documentclass{article}
\usepackage{etoolbox}

\newcommand*\listone{}
\newcommand*\listtwo{}

\forcsvlist{\listadd\listone}{A,B,C}
\forcsvlist{\listadd\listtwo}{x,y,y}

\def\do#1#2{\hyperref[#1]{#2}}

% \dotwolistloop{\listone}{\listwo} This is the instruction I am looking for.

\end{document}

In reality, I need to be able to process three lists simultaneously, so please hints to solutions what would be easy to extend to more than two lists.

  • There are already two answers -- I don't think this is possible with etoolbox at all. – user31729 Mar 6 '16 at 6:16
3

this iterates through three lists, executing the three argument command \testlist at each stage.

enter image description here

\documentclass{article}

\def\listone{A,B,C}
\def\listtwo{x,y,z}
\def\listthree{1,2,3}

\newcommand\testlist[3]{[#1][#2][#3]\par}
\makeatletter

\def\loopthree#1#2#3{%
\edef\tmp{\noexpand\xthree
\unexpanded\expandafter{#1},\relax
\unexpanded\expandafter{#2},\relax
\unexpanded\expandafter{#3},\relax
}\tmp}


\def\xthree#1,#2\relax#3,#4\relax#5,#6\relax{%
\testlist{#1}{#3}{#5}%
\def\tmp{#2}%
\ifx\empty\tmp
\expandafter\@gobble
\else
\expandafter\@firstofone
\fi
{\xthree#2\relax#4\relax#6\relax}}

\makeatother

\begin{document}

\loopthree\listone\listtwo\listthree

\end{document}
  • Perhaps, it would be better to use a delimiter, like \@@@, that it is not as easy for a user to include in a list by accident as \relax is. – GuM Mar 5 '16 at 22:58
  • @GustavoMezzetti yes actually in this form the delimiter is never tested with \ifx so can be any unique csname, so as you say something other than \relax could be used. – David Carlisle Mar 5 '16 at 23:03
  • Thanks @David Carlisle. With your help, I was able to make the above loopthree macro be compatible with lists created by etoolbox. I added an "answer" below. – Denis Cousineau Mar 7 '16 at 20:24
1

You can use expl3 and xparse:

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn

\NewDocumentCommand{\addtolist}{mm}
 {% #1 = list name; #2 = items
  \seq_clear_new:c { l_denis_list_#1_seq }
  \clist_map_inline:nn { #2 }
   {
    \seq_put_right:cn { l_denis_list_#1_seq } { ##1 }
   }
 }
\NewDocumentCommand{\twolistloop}{mmm}
 {% #1 = first list name; #2 = second list name; #3 = two argument macro
  \seq_mapthread_function:ccN { l_denis_list_#1_seq } { l_denis_list_#2_seq } #3
 }
\ExplSyntaxOff

\newcommand{\makehref}[2]{\href{#1}{#2}\par}

\addtolist{texts}{\TeX\ @ SX,Google,Yahoo}
\addtolist{urls}{
  http://tex.stackexchange.com,
  http://google.com,
  http://yahoo.com
}

\begin{document}

\twolistloop{urls}{texts}{\makehref}

\end{document}

The macro \addtolist appends items to a sequence, creating it if it doesn't exist yet. The \twolistloop macro uses two sequences, passing an item from each to the two parameter macro given as third argument.

In the example I used \href, by hiding it in another macro in order to be able to add \par at the end of each call.

Each line is a link to the corresponding site.

enter image description here

0

With the solution proposed by David Carlisle above, I changed a little the macro so that it works with lists created from etoolbox. The only two differences are that (1) etoolbox lists are separated with a catcode 3 | character (instead of a comma) (2) the list already ends with a delimiters (whereas David above manually added one last comma at the end of the lists). So the whole code is

\documentclass{article}

\usepackage{etoolbox}

% with etoolbox lists
\newcommand*\listone{}
\newcommand*\listtwo{}
\newcommand*\listthree{}
\forcsvlist{\listadd\listone}{A,B,C}
\forcsvlist{\listadd\listtwo}{x,y,y}
\forcsvlist{\listadd\listthree}{1,2,3}

\makeatletter
  \catcode`\|=3

  \def\loopthree#1#2#3{%
    \edef\tmp{\noexpand\xthree
      \unexpanded\expandafter{#1}\relax  % no added delimiter here
      \unexpanded\expandafter{#2}\relax  % no added delimiter here
      \unexpanded\expandafter{#3}\relax  % no added delimiter here
    } \tmp
  }

  \def\xthree#1|#2\relax#3|#4\relax#5|#6\relax{%
    \dothree{#1}{#3}{#5}%
    \def\tmp{#2}%
    \ifx\empty\tmp
      \expandafter\@gobble
    \else
      \expandafter\@firstofone
    \fi
    {\xthree#2\relax#4\relax#6\relax}
  }

  \catcode`\|=12
\makeatother

\begin{document}

% as per David answer:
\newcommand\dothree[3]{[#1][#2][#3]\par}
\loopthree\listone\listtwo\listthree

% as a new example of the use of the dothree macro
Here is all three lists:
\renewcommand\dothree[3]{\item #1 + #2 + #3}
\begin{itemize}
  \loopthree\listone\listtwo\listthree
\end{itemize}

\end{document}

Thank you very much for your help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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