# How to check if a macro expands to one of several strings?

I have the following problem: I am using the etoolbox package to check if a macro (say, \foo) expands to one of several possible strings (say, bar, baz, abc, or other). I need to execute different code depending on what string \foo expands to.

My current solution is

\ifdefstring{\foo}{bar}
{% if \foo is bar
<do something>
}
{% if \foo is not bar
\ifdefstring{\foo}{baz}
{% if \foo is baz
<do something else>
}
{% if \foo is not bar or baz
\ifdefstring{\foo}{abc}
{% if \foo is abc
<do something else>
}
{% if \foo is not bar, baz, or abc
<do something else>
}
}
}


Although this works, it is not satisfactory due to the deep nesting, and it doesn't generalize well to longer lists of possible strings. In my real situation, I have up to eight possible strings to check, so this solution creates unreadable code.

My question is, what alternative options (in LaTeX2e) do I have to check if a macro expands to one of several strings?

Ideally, the solution would also work for other etoolbox comparison commands (e.g., \ifstrequal). However, the solution need not use the etoolbox package.

If \foo expands to a simple string, which survives a \csname, the following method can be used, which does not need LaTeX3:

\documentclass{article}

\makeatletter
\@namedef{foo@bar}{%
\typeout{\noexpand\foo is bar}%
% <do something>
}
\@namedef{foo@baz}{%
\typeout{\noexpand\foo is baz}%
% <do something>
}
\@namedef{foo@abc}{%
\typeout{\noexpand\foo is abc}%
% <do someting>
}

\newcommand*{\evaluate@foo}{%
\@ifundefined{foo@\foo}{%
\errmessage{\noexpand\foo has unknown value}%
}{%
\@nameuse{foo@\foo}%
}%
}
\providecommand*{\foo}{}% initialization

% Test

\def\foo{baz}
\evaluate@foo

\def\foo{abc}
\evaluate@foo

\def\foo{xyz}
\evaluate@foo

\makeatletter

\begin{document}
\end{document}


Result:

\foo is baz
\foo is abc
! \foo has unknown value.

• This looks like it might do the trick! I'll try it out later today and accept your answer if it does. Thanks! Sep 10 '15 at 23:44
• This idea ended up working perfectly, thanks again! Sep 11 '15 at 3:40

The best solution I can think of is expl3’s \str_case:nnTF. Here I generate a variant to take one token instead of a test string and compares to expansion of that token to the list. If no match is found in the list an appropriate message is output.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\cs_generate_variant:Nn \str_case:nnF { VnF }

\cs_new_protected:Npn \artem_testfoo:N #1
{
\texttt{ \cs_to_str:N #1 } ~
\str_case:VnF #1
{
{ bar } { is ~ bar }
{ baz } { is ~ baz }
{ abc } { is ~ abc }
}
{ is ~ neither ~ bar, ~ nor ~ baz, ~ nor ~ abc. }
}

\NewDocumentCommand \testfoo { m }
{
\artem_testfoo:N #1
}

\ExplSyntaxOff
\begin{document}

\def\foo{bar}
\testfoo\foo

\def\foo{baz}
\testfoo\foo

\def\foo{abc}
\testfoo\foo

\def\foo{xyz}
\testfoo\foo

\end{document}


• BTW: \cs_generate_variant:Nn \str_case:nnF { V } would suffice :) Sep 10 '15 at 19:45
• Thank you for taking time to answer! Unfortunately, I'm not familiar with LaTeX3 at all (I should have specified that in the question). Sep 10 '15 at 23:46

It's quite easy with xparse and expl3:

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\xifstring}{mO{}m}
{
\str_case:onF { #1 } { #3 } { #2 }
}
\ExplSyntaxOff

\begin{document}

\newcommand{\foo}{abc}

\xifstring{\foo}[No match]
{
{abc}{String is abc'}
{def}{String is def'}
{ghi}{String is ghi'}
}

\renewcommand{\foo}{def}

\xifstring{\foo}[No match]
{
{abc}{String is abc'}
{def}{String is def'}
{ghi}{String is ghi'}
}

\renewcommand{\foo}{ghi}

\xifstring{\foo}[No match]
{
{abc}{String is abc'}
{def}{String is def'}
{ghi}{String is ghi'}
}

\renewcommand{\foo}{foo}

\xifstring{\foo}[No match]
{
{abc}{String is abc'}
{def}{String is def'}
{ghi}{String is ghi'}
}

\end{document}


The optional argument is for what you want when there's no match (default value, do nothing).

• Thank you for taking time to answer! Unfortunately, I'm not familiar with LaTeX3 at all (I should have specified that in the question). Sep 10 '15 at 23:46
• Why do you need to be familiar with latex3? Just think of it as a package that you load that provides programming tools. Sep 11 '15 at 0:58

The simple minded method from my other answer avoids nesting. Here we don't have TeX like \if..\fi but suitable wrappers, so we need to modify a bit the definitions.

\long\def\DoThis #1#2\OrThat #3{#1}
\long\def\OrThat #1{#1}% \@firstofone

\ifdefstring{\foo}{bar}{\DoThis{do something}}{}%
\ifdefstring{\foo}{baz}{\DoThis{do something else}}{}%
\ifdefstring{\foo}{bat}{\DoThis{do something different}}{}%
\OrThat {in case all tests fail}%


Remark: once a test is positive all successive ones are skipped, due to the definition of \DoThis macro.

• the method applies to all possible branching conditional of the type \ifsometest{..}...{YES}{NO} .
– user4686
Sep 11 '15 at 11:30