3

Is there a way to write a macro that steps through two lists of arguments pairwise, like what Python's zip function does? For example, I'd like to write a macro like this (using xparse because that's what I've been experimenting with; I'm open to other options):

\NewDocumentCommand{\Zip}{ >{\SplitList{,}}m >{\SplitList{,}}m }{%
    % something that calls \myfunc in an itemize environment
}

\newcommand{\myfunc}[2]{\item #1, #2}

So that calling Zip{zebra,frog,jay}{mammal,amphibian,bird} is equivalent to

\begin{itemize}
    \item zebra, mammal
    \item frog, amphibian
    \item jay, bird
\end{itemize}

Is this possible? I've looked into xparse but it seems to only allow stepping through two lists one at a time, not zipping them together. There was a similar question here back in 2011, but I hope there's a nicer solution now.

gil
  • 133

2 Answers2

3

You can use \seq_mapthread_function:NNN to iterate over two seq variables like that.

enter image description here

\documentclass{article}

\ExplSyntaxOn \seq_new:N \l__gil_tmpa_seq \seq_new:N \l__gil_tmpb_seq \NewDocumentCommand \Zip { m m } { \seq_set_from_clist:Nn \l__gil_tmpa_seq {#1} \seq_set_from_clist:Nn \l__gil_tmpb_seq {#2} \seq_mapthread_function:NNN \l__gil_tmpa_seq \l__gil_tmpb_seq \myfunc } \ExplSyntaxOff

\newcommand{\myfunc}[2]{\item #1, #2}

\begin{document}

\begin{itemize} \Zip{zebra,frog,jay}{mammal,amphibian,bird} \end{itemize}

\end{document}

  • Nice, exactly what I need! I'll see if someone has a higher-level solution (I was kind of expecting there to be one) but if not, I will accept your answer. – gil Oct 27 '20 at 18:05
  • @gil How higher-level do you mean? \seq_mapthread_function:NNN is a documented interface in expl3, so it is really not “low level”. If you mean something built into xparse, then the answer is no, it doesn't have that. – Phelype Oleinik Oct 27 '20 at 18:14
  • I mean if some package offers an easy, xparse-like interface for zipping (which xparse doesn't do, I know), I'd prefer that. Maybe I was hoping for too much. – gil Oct 27 '20 at 21:23
  • @gil It's rather specific, so I don't think so (but there are thousands of packages out there, so there might as well be, but none that I know of). There's \foreach from the pgffor package, which is quite powerful and maybe has something to do this (I know it can do a for loop with two counters, like C's for; I don't know about arbitrary lists). – Phelype Oleinik Oct 27 '20 at 21:32
  • This is essentially identical to my answer to the question marked as duplicate, although not as powerful. – egreg Oct 28 '20 at 18:20
  • How could I make this work in case I put the two lists in a command first? So e.g. having \newcommand{\listone}{zebra,frog,jay} and \newcommand{\listtwo}{mammal,amphibian,bird}, then calling \Zip{\listone}{\listtwo}? – Steven Mar 15 '23 at 16:15
  • @Steven Replace the two \seq_set_from_clist:Nn with \seq_set_from_clist:NN (:NN instead of :Nn) – Phelype Oleinik Mar 15 '23 at 16:24
  • @PhelypeOleinik thanks for the quick reply, works like a charm! – Steven Mar 15 '23 at 17:13
0

Using \foreach from pgffor, to illustrate as per the comment.

Traverse the diagonal of the matrix of pairwise combinations:

\foreach \x [count=\xi] in {#1}{%
\foreach \y [count=\yi] in {#2}{%
\ifnum \xi=\yi%
\item \x , \y 
\fi
}
}

zipped pairs

MWE


\documentclass{article}
\usepackage{xparse}
\usepackage{pgffor}

\ExplSyntaxOn \seq_new:N \l__gil_tmpa_seq \seq_new:N \l__gil_tmpb_seq \NewDocumentCommand \Zip { m m } { \seq_set_from_clist:Nn \l__gil_tmpa_seq {#1} \seq_set_from_clist:Nn \l__gil_tmpb_seq {#2} \seq_mapthread_function:NNN \l__gil_tmpa_seq \l__gil_tmpb_seq \myfunc } \ExplSyntaxOff

\newcommand{\myfunc}[2]{\item #1, #2}

\newcommand{\myfuncb}[2]{% \begin{itemize} \foreach \x [count=\xi] in {#1}{% \foreach \y [count=\yi] in {#2}{% \ifnum \xi=\yi% \item \x , \y \fi } } \end{itemize} }

\begin{document}

Expl3: \begin{itemize} \Zip{zebra,frog,jay}{mammal,amphibian,bird} \end{itemize}

\textbackslash foreach: \myfuncb{zebra,frog,jay}{mammal,amphibian,bird}

\end{document}

Cicada
  • 10,129