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.

I frequently need to do something like this:

IF x < 1, DO a
ELSE IF x < 2, DO b
ELSE IF x < 3, DO c
ELSE, DO d

Using etoolbox, I end up nesting a lot of ifnumless, like this:

\ifnumless{x}{1}{a}{
    \ifnumless{x}{2}{b}{
        \ifnumless{x}{3}{c}{
            d
        }
    }
}

Is there a simpler way to achieve this?

share|improve this question
1  
This thread might help: tex.stackexchange.com/questions/17676/… –  Paulo Cereda Nov 26 '11 at 1:34
    
Well, TeX (and eTeX) has no \elseif, \elif; and needs \fi for every \if. So usually we always write bad-looking code. –  Leo Liu Nov 26 '11 at 7:34
add comment

6 Answers

up vote 14 down vote accepted

A kind of "switchcase" can be easily programmes:

\documentclass{article}
\makeatletter
\def\ifnumcase#1{%
    \edef\elseif@{\string\elseif}\edef\endif@{\string\endif}%
    \def\number@test{#1pt}\ifnumcase@i}
\def\ifnumcase@i#1{%
    \def\ifnumcase@ii##1{%
        \csname
            @\ifdim\number@test#1pt first\else second\fi oftwo%
        \endcsname{##1\gobto@endif}\ifnumcase@i}%
    \edef\valeur@{\string#1}%
    \csname
        \ifx\valeur@\elseif@ idto@endif%
        \else\ifx\valeur@\endif@ relax\else ifnumcase@ii\fi
        \fi
    \endcsname}
\def\idto@endif#1\endif{#1}
\def\gobto@endif#1\endif{}
\makeatother
\begin{document}
\ifnumcase{7.5}% <- number to test
    {<1}{less than 1}
    {<3}{less than 3}
    {>5}{greater than 5}
    % add other tests if needed
\elseif
    between 3 and 5% all tests faild
\endif

\ifnumcase{3.14}% <- number to test
    {<2}{less than 1}
    {<3}{less than 3}
    {<4}{less than 4}
    {<10}{less than 10}
    % add possible other tests
\endif
\end{document}

EDIT: an even more complete and expandable solution with =, <, >, <= or <= tests:

\documentclass{article}
\usepackage[T1]{fontenc}
\makeatletter
\def\elseif{\elseif}\def\endif{\endif}
\def\ifnumcase#1#2{%
    \expandafter\ifx\expandafter\elseif\@car#2\@nil\expandafter\@firstoftwo
    \else
        \expandafter\ifx\expandafter\endif\@car#2\@nil\expandafter\expandafter\expandafter\@gobbletwo
        \else\expandafter\expandafter\expandafter\@secondoftwo
        \fi
    \fi
        \idto@endif
        {\if@eqin#2=\@nil{\if@dimwitheq{#1}#2\@nil}{\ifdim#1pt#2pt }
            \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi\exec@arg{\ifnumcase@i{#1}}%
        }%
    }
\def\ifnumcase@i#1#2{\ifnumcase{#1}}
\def\if@eqin#1=#2\@nil{%
    \ifx\@empty#2\@empty\expandafter\@secondoftwo
    \else
        \ifx\@empty#1\@empty\expandafter\expandafter\expandafter\@secondoftwo
        \else\expandafter\expandafter\expandafter\@firstoftwo
        \fi
    \fi}
\def\if@dimwitheq#1#2=#3\@nil{\unless\ifdim#1pt\if<#2>\else<\fi#3pt }
\def\exec@arg#1#2\endif{#1}
\def\idto@endif#1\endif{#1}
\def\gobto@endif#1\endif{}
\makeatother
\begin{document}
\ifnumcase{3}
    {<=1}{lt or equal 1}
    {>=3}{gt or equal 3}
\elseif
    beteween 1 and 3
\endif

\ifnumcase{4}% <- number to test
    {<=1}{less or equal 1}
    {<=3}{less or equal 3}
    {=4}{equal 4}
    {>=5}{greater or equal 5}
    % add other tests if needed
\elseif
    between 3 and 5% all tests faild
\endif

\edef\foo{\ifnumcase{5}% <- number to test
    {<=1}{less or equal 1}
    {<=3}{less or equal 3}
    {=4}{equal 4}
    {>=5}{greater or equal 5}
    % add other tests if needed
\elseif
    between 3 and 5% all tests faild
\endif}\meaning\foo

\ifnumcase{3.14}% <- number to test
    {<2}{less than 1}
    {<3}{less than 3}
    {<4}{less than 4}
    {<10}{less than 10}
    % add possible other tests
\endif
\end{document}
share|improve this answer
    
What of \def\x{} \edef\x{% \ifsomecase\ifx\x \y{equal to \string\y} \z{equal to \string\z} % add possible other tests \elseif equal to none% \endif } \edef\x{% \ifsomecase\ifnum{2.1} {<=2}{equal or less than 2} {>=4}{equal or greater than 4} % add possible other tests \elseif equal to none% \endif } –  Ahmed Musa Nov 26 '11 at 21:39
    
"village" wanted to compare numbers... –  unbonpetit Nov 26 '11 at 22:13
    
But you've already exceeded what he wanted. We're now dealing with the more interesting exercises. :) Actually, adding \ifx, \ifstrcmp, etc. is routine. \def\elseif{\elseif} is like LaTeX3 quark. Use with care. –  Ahmed Musa Nov 26 '11 at 22:23
    
I don't think I have exceeded what he wants. After all, I have implemented tests on numbers. For sure, expandibility is probably a funny feature he didn't ask. I do not know what is a latex3 quark. Where in my code do you think I am careless? –  unbonpetit Nov 26 '11 at 22:48
add comment

Depending on who else needs to use the code, would lualatex work?

\documentclass{article}
\begin{document}
\directlua{
x=1.5
}
\directlua{
if x<1 then
  tex.print('a')
elseif x<2 then
  tex.print('b')
elseif x<3 then
  tex.print('c')
else
  tex.print('d')
end
}
\end{document}
share|improve this answer
    
I do not think LuaTeX will fit my needs well, as I always work with Unicode text. –  Village Nov 26 '11 at 3:17
3  
@Village: Huh? Luatex works perfectly well with unicode. –  Aditya Nov 26 '11 at 6:56
    
I thought that LuaTeX uses unicode by design... –  qubyte Nov 26 '11 at 10:12
add comment

If you have it only to compare with integers then it is easy:

\documentclass{minimal}   
\def\getInteger#1{\expandafter\stripDecimals#1..!!}
\def\stripDecimals#1.#2.#3!!{\ifx\relax#1\relax0\else#1\fi}

\def\x{\getInteger{0.5}}      
\begin{document}   

\ifcase\x   % 0
 lt 1\or    % 1
 lt 2\or    % 2
 lt 3       % 3
\else gt 3  % else
\fi

\end{document}
share|improve this answer
add comment

Best Practices ? Question not specific enough, this depends of the engine, then you want to use TeX, LaTeX or LuaTeX etc. You want a concise code or fast code?

With TeX something like the code below is possible (there is other possibilities for example without \ifdim

\documentclass[11pt]{scrartcl}   
\begin{document}   
\def\x{1.5}   

\ifdim \x pt< 1pt a
   \else 
     \ifdim \x pt < 2 pt b
       \else  
          \ifdim \x pt < 3pt c
            \else  d  
           \fi
     \fi
\fi
\end{document}  
share|improve this answer
add comment

An expandable and more complete 'switch' solution is:

\documentclass{article}
\makeatletter
\def\identoendif#1\endif{#1}
\def\oneoftoendif#1#2\endif{#1}
\def\gobbletoendif#1\endif{}
\newcommand*\ifstrsame[2]{%
  \@nameuse{@\ifnum\pdfstrcmp{\detokenize{#1}}%
    {\detokenize{#2}}=0first\else second\fi oftwo}%
}
\def\dimexpr@i#1{#1\dimexpr}
\def\ifnumcase#1{%
  \ifstrsame{#1}\elseif\gobbletoendif{%
    \ifstrsame{#1}\endif\@gobble\ifnumcase@i
  }{#1}%
}
\def\ifnumcase@i#1#2{%
  \ifstrsame{#2}\elseif\identoendif{%
    \ifstrsame{#2}\endif{}{%
      \ifdim\dimexpr#1pt\relax\dimexpr@i#2pt\relax
        \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
          {\oneoftoendif}{\ifnumcase@ii{#1}}%
    }%
  }%
}
\def\ifnumcase@ii#1#2#3{\ifnumcase@i{#1}{#3}}
\makeatother

%% Examples:
\edef\x{%
  \ifnumcase{6}% <- number to test
    {<1}{less than 1}
    {<3}{less than 3}
    {>5}{greater than 5}
    % add other tests if needed
  \elseif
    between 3 and 5% all tests failed
  \endif
}
\show\x -> greater than 5

\edef\x{%
  \ifnumcase{3.14}% <- number to test
    {<2}{less than 1}
    {<3}{less than 3}
    {<4}{less than 4}
    {<10}{less than 10}
    % add possible other tests
  \endif
}
\show\x -> less than 4

\edef\x{%
  \ifnumcase{314}% <- number to test
    {<2}{less than 1}
    {<3}{less than 3}
    {<4}{less than 4}
    {<10}{less than 10}
    % add possible other tests
  \endif
}
\show\x -> empty

\begin{document}
% Nothing to do:
\ifnumcase\elseif\endif
\ifnumcase{3}\elseif\endif
\ifnumcase\endif
\ifnumcase{3}\endif
\end{document}

unbonpetit's latest solution fails for

\ifnumcasse{3}
    {=<1}{lt or equal 1}
    {=>3}{gt or equal 3}
\elseif
    beteween 1 and 3
\endif

and

\def\gobto@endif#1\endif{}

is redundant.

A more general solution is

\makeatletter
\def\elseif{\@gobble\elseif}
\def\endif{\@gobble\endif}
\def\swap#1#2{#2#1}
\def\ifstrsame#1#2{%
  \ifnum\pdfstrcmp{\detokenize{#1}}{\detokenize{#2}}=\z@
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\def\ifstrnull#1{%
  \ifnum\pdfstrcmp{\detokenize{#1}}{}=\z@
  \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\def\ifcmdeq#1#2{%
  \ifx#1#2\endif\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
}
\def\if@eqin#1=#2\@nil{%
  \ifstrnull{#2}\@secondoftwo{\ifstrnull{#1}\@secondoftwo\@firstoftwo}%
}
\def\if@dimwitheq#1#2=#3\@nil{\unless\ifdim#1pt\if<#2>\else<\fi#3pt }
\def\docasecallback#1#2\endif{#1}
\def\doelsepart#1\endif{#1}
\def\ifnumcase#1#2{%
  \expandafter\ifcmdeq\@car#2\@nil\elseif\@firstoftwo{%
    \expandafter\ifcmdeq\@car#2\@nil\endif\@gobbletwo\@secondoftwo
  }%
  \doelsepart{%
    \expandafter\expandafter\expandafter
    \if@eqin\checkcomparators#2\@nil=\@nil{%
      \expandafter\expandafter\expandafter\swap
      \expandafter\expandafter\expandafter
      {\checkcomparators#2\@nil}{\if@dimwitheq{#1}}\@nil
    }{%
      \ifdim#1pt#2pt %
    }
    \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    \docasecallback{\ifnumcase@i{#1}}%
  }%
}
\def\ifnumcase@i#1#2{\ifnumcase{#1}}
\def\checkcomparators#1#2#3\@nil{%
  \romannumeral
  \ifstrsame{#1}={\ifstrsame{#2}<{0<=#3}{\ifstrsame{#2}>{0>=#3}{0 #1#2#3}}%
  }{0 #1#2#3}%
}
\makeatother
share|improve this answer
    
I do not find your little game funny. Ok, you win, you proove that you are much better than me and your solution are much more "general". Bye. –  unbonpetit Nov 27 '11 at 12:11
    
@unbonpetit Oh, I thought it was just another online game. Nothing to prove and nothing has been proved! Sorry. Bye, bye. –  Ahmed Musa Nov 27 '11 at 17:03
1  
An online game??? Is it a game for you to give "better" or "more general" solutions than me? To say that my code "fails", that it contains "redundant" parts? For sure, it is a very easy to find a case in which a code does not work. It is probably funny to suggest that I am not careful enough with "quarks". Let me say that I answered the question because I just wanted to help "Village". Nothing esle. I was not looking for someone to correct me. And I was not looking for someone to compete with. You misunderstood my motivation! –  unbonpetit Nov 27 '11 at 18:29
add comment

Here's a solution to the problem of some elseif equivalent that is undoubtedly inefficient and probably encompasses numerous bad practices (including using the archaic ifthen package and using \else as a delimiter), but is very short and seems to work.

\RequirePackage{ifthen}
\def\fifi\relax
\newcommand{\ifif}{}             % I have no idea why this line is needed.
\long\def\ifif#1#2\else#3\fifi{\ifthenelse{#1}{#2}{#3\fifi}}

A usage example (which makes a lot more sense when you use SageTeX):

\documentclass{article}
\usepackage{mathtools}
\DeclarePairedDelimiter\floor{\lfloor}{\rfloor}

\RequirePackage{ifthen}
\def\fifi\relax
\newcommand{\ifif}{}
\long\def\ifif#1#2\else#3\fifi{\ifthenelse{#1}{#2}{#3\fifi}}

\let \oldmbox=\mbox
\renewcommand{\mbox}[1]{%
    \ifif{\equal{floorHalfS}{#1}}%
        \floor*{\tfrac{1}{2}s}%
    \else\ifif{\equal{alphaS}{#1}}%
        \alpha(s)%
    \else\ifif{\equal{lprime}{#1}}%
        \ell'%
    \else\ifif{\equal{tprime}{#1}}%
        t'%
    \else\ifif{\equal{qprime}{#1}}%
        q'%
    \else\ifif{\equal{ell}{#1}}%
        \ell%
    \else\ifif{\equal{kprime}{#1}}%
        k'%
    \else%
        \oldmbox{#1}%
    \fifi%
}


\begin{document}

\[\frac{\sqrt{\mbox{floorHalfS}^2 + 2\mbox{ell}}}{3\mbox{alphaS}}\]

\end{document}

The result:

enter image description here

share|improve this answer
add comment

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.