# Use \foreach loop to execute macro with parameters of macro provided in a list

I have to execute a macro a number of times, and would like to provide a list of the parameters to be executed. So, instead of

\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}


I would prefer to simply set a list:

\DefineMyFormatLinkParameters{%
*[Main Search Site]{Google}{http://www.google.com},
[]{Google}{http://www.google.com},
{Yahoo}{http://www.yahoo.com},
}


and then execute the \FormatLinks macro \foreach each element of this list. I think this is probably some expansion related issue, but I was not able to get it to work. Currently, the output is:

## Code:

\documentclass{article}
\usepackage{url}
\usepackage{pgffor}
\usepackage{xparse}
\usepackage{xstring}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
s%   #1 =* not used yet
O{}% #2 = optional title
m%   #3 = Mandatory title
m%   #4 = URL Link
}{%
\par
\IfStrEq{#2}{}{%
\hspace*{1.0cm}\href{#4}{#3}%
}{%
\hspace*{1.0cm}\href{#4}{#3~(#2)}%
}%
}%

\newcommand*{\MyFormatLinkParameters}{}% Initialize
\newcommand*{\DefineMyFormatLinkParameters}[1]{%
\edef\MyFormatLinkParameters{#1}%
}%

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{%
*[Main Search Site]{Google}{http://www.google.com},
[]{Google}{http://www.google.com},
{Yahoo}{http://www.yahoo.com},
}

\bigskip%
Following should produce same results as above:\medskip\par
\foreach \x in \MyFormatLinkParameters {%
\typeout{DEBUG: "\x"}
\FormatLinks{\x}
}

\end{document}


## 2 Answers

Two things are wrong here:

1. Expansion of \x should be done and not passed as a single argument to \FormatLinks.

So, instead of

\FormatLinks{\x}


use

\expandafter\FormatLinks\x


since this will leave an expanded \x in the input stream for \FormatLinks to gobble up.

2. You have an empty item in the list (the last item);

The empty list item implies that when you're passing \x to \FormatLinks it is expecting at least two arguments yet it receives none. So it gobbles whatever comes along and obviously does the wrong thing.

\documentclass{article}
\usepackage{url}
\usepackage{pgffor}
\usepackage{xparse}
\usepackage{xstring}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
s%   #1 =* not used yet
O{}% #2 = optional title
m%   #3 = Mandatory title
m%   #4 = URL Link
}{%
\par
\IfStrEq{#2}{}{%
\hspace*{1.0cm}\href{#4}{#3}%
}{%
\hspace*{1.0cm}\href{#4}{#3~(#2)}%
}%
}%

\newcommand*{\MyFormatLinkParameters}{}% Initialize
\newcommand*{\DefineMyFormatLinkParameters}[1]{%
\edef\MyFormatLinkParameters{#1}%
}%

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{%
*[Main Search Site]{Google}{http://www.google.com},
[]{Google}{http://www.google.com},
{Yahoo}{http://www.yahoo.com}
}

\bigskip%
Following should produce same results as above:\medskip\par
\foreach \x in \MyFormatLinkParameters {%
\typeout{DEBUG: "\x"}
\expandafter\FormatLinks\x
}

\end{document}

• Thanks. I had tried \expandafter\FormatLinks{\x}, but did not try \expandafter\FormatLinks\x. – Peter Grill Apr 15 '15 at 23:22

Why the mysterious \IfStr{#2}{} when xparse already provides the infrastructure?

\NewDocumentCommand{\FormatLinks}{%
s%   #1 =* not used yet
o%   #2 = optional title
m%   #3 = Mandatory title
m%   #4 = URL Link
}{%
\par
\hspace*{1.0cm}\href{#4}{#3\IfValueT{#2}{~#2}}%
}


The problem is that you don't want to do \FormatLinks{\x} but want to expand \x before \FormatLinks sees it; however the braces should not be there. So \expandafter\FormatLinks\x would do.

Better, use a clist and a mapping function.

\documentclass{article}
\usepackage{url}
\usepackage{xparse}
\usepackage[colorlinks=true]{hyperref}

\NewDocumentCommand{\FormatLinks}{%
s%   #1 =* not used yet
o%   #2 = optional title
m%   #3 = Mandatory title
m%   #4 = URL Link
}{%
\par
\hspace*{1.0cm}\href{#4}{#3\IfValueT{#2}{~#2}}%
}

\ExplSyntaxOn
\NewDocumentCommand{\FormatLinksList}{}
{
\clist_map_inline:Nn \g_grill_linkparameters_clist { \FormatLinks ##1 }
}
\NewDocumentCommand{\DefineMyFormatLinkParameters}{m}
{
\clist_gset:Nn \g_grill_linkparameters_clist { #1 }
}
\ExplSyntaxOff

\begin{document}
\FormatLinks*[Main Search Site]{Google}{http://www.google.com}
\FormatLinks[]{Google}{http://www.google.com}
\FormatLinks{Yahoo}{http://www.yahoo.com}

% Would prefer to define a list, and later execute the list:
\DefineMyFormatLinkParameters{
*[Main Search Site]{Google}{http://www.google.com},
[]{Google}{http://www.google.com},
{Yahoo}{http://www.yahoo.com},
}

\bigskip

Following should produce same results as above:\medskip\par

\FormatLinksList

\end{document}


• I didn't know that \IfValueT would properly handle the case where the user specified []. That is really helpful to know. – Peter Grill Apr 15 '15 at 23:28
• @PeterGrill It returns true only if the user specifies [...]. There are also \IfValueF and \IfValueTF; \IfValueT is equivalent to \IfNoValueF, but in this case it's semantically preferable the “positive” one. – egreg Apr 15 '15 at 23:31