27

I am attempting to ignore a command if it is passed with empty/blank text. My use case is not as complicated as one Expand away empty macros within ifthenelse where @egreg provides what seems like a great way to test if the text passed to it would produce no output. However, I can't seem to get it to work.

When the following works the output should be just the name (if no address or an empty/blank address is given):

Name: Peter's Pizza

or if a non-blank address is given:

Name: Peter's Pizza

Address: 123 Main Street, Anytown, USA

xstring Version of IfNoText:

With my original version (with %\def\UseEgregsIfNoText{} commented out), Section 1 and 3 are correct.

Section 2 does not work properly in that the Address: is printed when it should not be. This xstring version of \IfNoText also does not allow me to have blank lines in the \SetAddress{} (see second occurrence of \UseEgregsIfNoText) which I would like.

Furthermore, Section 4 shows that I am not able to compile if I attempt to access \MandatoryName. Note that it is commented out in this case.

enter image description here

Egreg's Version of IfNoText:

With @egreg's version of \IfNoText (uncomment \def\UseEgregsIfNoText{}) obtained from Expand away empty macros within ifthenelse, I can compile with blank lines in the \SetAddress{} and use the \MandatoryName macro, but don't get the desired results. Here Case 1 and 2 are correct, but not 3 (which has additional blank lines inserted in \SetAddress) and 4 (which attempts to access the value of \MandatoryName).

enter image description here

Notes:

  • I was planning to use \IgnoreSpacesAndImplicitePars and \IgnoreSpacesAndAllPars from Looking for an \ignorespacesandpars, but they don't seem to make a difference. These macros are included in the code below but are not used
  • egreg does warn that tricky cases might fool ``, but I don't think that blank lines was what he meant.

Code:

\def\UseEgregsIfNoText{}%

\documentclass{article} \usepackage{xspace} \usepackage{xstring}

% https://tex.stackexchange.com/questions/23100/looking-for-an-ignorespacesandpars/23110#23110 \makeatletter \def\IgnoreSpacesAndImplicitePars{% Not used \begingroup \catcode13=10 @ifnextchar\relax {\endgroup}% {\endgroup}% }

\def\IgnoreSpacesAndAllPars{% Not used \begingroup \catcode13=10 @ifnextchar\par {\endgroup\expandafter\IgnoreSpacesAndAllPars@gobble}% {\endgroup}% } \makeatother

\ifdefined\UseEgregsIfNoText %% https://tex.stackexchange.com/questions/42280/expand-away-empty-macros-within-ifthenelse \newcommand{\IfNoText}[3]{% \sbox0{#1}% \ifdim\wd0=0pt % {#2}% if #1 is empty \else% {#3}% if #1 is not empty \fi% } \else \newcommand{\IfNoText}[3]{% %\edef\Parameter{\IgnoreSpacesAndAllPars#1} %\IfStrEq{\Parameter}{\empty}{#2}{#3}% \IfStrEq{#1}{\empty}{#2}{#3}% } \fi

\newcommand{\MandatoryName}{\empty}% \newcommand{\SetName}[1]{\renewcommand*{\MandatoryName}{#1\xspace}}%

\newcommand{\OptionalAddress}{\empty}% can have line breaks, so no "*" \newcommand{\SetAddress}[1]{% \IfNoText{#1}{% % No printable text so ignore... }{% \renewcommand{\OptionalAddress}{\ignorespaces#1}% }% }%

\newcommand*{\ShowNameAndAddress}{% \par\noindent\textbf{Name:}~\MandatoryName \IfNoText{\OptionalAddress}{}{% \par\noindent\textbf{Address:}~\OptionalAddress }% }% \begin{document} \section{Name with no Address} \SetName{Peter's Pizza} \ShowNameAndAddress

\section{Name with Empty address} \ifdefined\UseEgregsIfNoText % Want to be able to handle this: \SetAddress{

}

\else \SetAddress{ } \fi

\ShowNameAndAddress

\section{Name with Address Given}

\SetAddress{ 123 Main Street, Anytown, USA }

\ShowNameAndAddress

\section{Name with Address using Name}

%Verify: \verb|\MandatoryName =| \MandatoryName

\ifdefined\UseEgregsIfNoText % Want to be able to access value of \MandatoryName here \SetAddress{ 123 \MandatoryName Way, Anytown, USA } \else % Can't even compile in this case with \MandatoryName \SetAddress{ 123 %\MandatoryName Way, Anytown, USA } \fi

\ShowNameAndAddress

\end{document}

Peter Grill
  • 223,288

5 Answers5

9

Here's how I would write it.

\makeatletter
\newcommand{\DoIfNoText}[1]{%
  \begingroup
  \sbox0{#1}%
  \ifdim\wd0=\z@
    \endgroup
    \expandafter\@gobble
  \else
    \endgroup
    \expandafter\@firstofone
  \fi}
\makeatother
\newcommand{\MandatoryName}{}
\newcommand{\SetName}[1]{\renewcommand{\MandatoryName}{#1}}

\newcommand{\OptionalAddress}{}
\newcommand{\SetAddress}[1]{%
  \DoIfNoText{#1}
    {\renewcommand{\OptionalAddress}{\ignorespaces#1}}%
  }

\newcommand{\ShowNameAndAddress}{%
  \par\noindent\textbf{Name:}~\MandatoryName
  \DoIfNoText{\OptionalAddress}
    {\par\noindent\textbf{Address:}~\OptionalAddress}%
  }

Probably

\def\OptionalAddress{}

should be added to \ShowNameAndAddress if you want to avoid the meaning of \OptionalAddress to be carried over to the next address.

Blank lines in the argument of \DoIfNoText are irrelevant, as the box is built in restricted horizontal mode, where \par commands are ignored.

Note I've added a \begingroup-\endgroup pair to keep the assignment to \box0 local, thus avoiding possible conflicts in some situations (see Missing Item number in enumerate and Frank Mittelbach's answer).

egreg
  • 1,121,712
  • This works great. But one thing I don't like about it is that the macro is defined as \newcommand{\DoIfNoText}[1], but takes two parameters. Does this not eliminate some error checking? Also, shouldn't this be called IgnoreIfNoText? – Peter Grill Feb 18 '12 at 16:19
  • It's not necessary to scan the following argument twice; and error checking is just the same. The second argument is actually either to \@gobble or \@firstofone. – egreg Feb 18 '12 at 16:29
6

I am not sure this solves your problem, but for the record: package xifthen offers isempty. You can define a macro like this:

\newcommand{\ifempty}[3]{%
  \ifthenelse{\isempty{#1}}{#2}{#3}%
}
Raphael
  • 5,983
3

The following things are worth mentioning:

  • Within \SetAddress, you redefine \OptionalAddress using

    \renewcommand{\OptionalAddress}{\ignorespaces#1}
    

    This definition only exists within \SetAddress. To see why this is the case, use \OptionalAddress just after setting the address, to see that nothing is typeset. So, you should rather use

    \gdef\OptionalAddress{\ignorespaces#1}
    

    which is short for \global\def. The reason being that you use \OptionalAddress in your \IfNoText test when calling \ShowNameandAddress.

  • A regular space spans 3.3333pt. However, it has no height or depth. So, perhaps you can change your definition of \IfNoText to

    \newcommand{\IfNoText}[3]{%
      \sbox0{#1}%
      \ifdim\wd0=0pt %
        {#2}% if #1 is empty
      \else%
        \ifdim0pt=\dimexpr\ht0+\dp0\relax
          {#2}% if #1 is empty
        \else
          {#3}% if #1 is not empty
        \fi
      \fi%
    

    which first checks to see if there is no width of the argument, followed by checking if there is no total height (height + depth), in the case of a space.

Including the above two suggestions yields:

enter image description here

\def\UseEgregsIfNoText{}% 

\documentclass{article}
\usepackage{xspace}
\usepackage{xstring}

% http://tex.stackexchange.com/questions/23100/looking-for-an-ignorespacesandpars/23110#23110
\makeatletter
\def\IgnoreSpacesAndImplicitePars{%  Not used
  \begingroup
  \catcode13=10
  \@ifnextchar\relax
    {\endgroup}%
    {\endgroup}%
}

\def\IgnoreSpacesAndAllPars{%       Not used
  \begingroup
  \catcode13=10
  \@ifnextchar\par
    {\endgroup\expandafter\IgnoreSpacesAndAllPars\@gobble}%
    {\endgroup}%
}
\makeatother

\ifdefined\UseEgregsIfNoText
    %% http://tex.stackexchange.com/questions/42280/expand-away-empty-macros-within-ifthenelse
    \newcommand{\IfNoText}[3]{%
        \sbox0{#1}%
        \ifdim\wd0=0pt %
            {#2}% if #1 is empty
        \else%
          \ifdim0pt=\dimexpr\ht0+\dp0\relax
            {#2}
          \else
            {#3}% if #1 is not empty
          \fi
        \fi%
    }
\else
    \newcommand{\IfNoText}[3]{%
        %\edef\Parameter{\IgnoreSpacesAndAllPars#1}
        %\IfStrEq{\Parameter}{\empty}{#2}{#3}%
        \IfStrEq{#1}{\empty}{#2}{#3}%
    }
\fi


\newcommand*{\MandatoryName}{\empty}%
\newcommand*{\SetName}[1]{\renewcommand*{\MandatoryName}{#1\xspace}}%

\newcommand{\OptionalAddress}{\empty}% can have line breaks, so no "*"
\newcommand{\SetAddress}[1]{%
    \IfNoText{#1}{% 
        % No printable text so ignore...
    }{%
        \gdef\OptionalAddress{\ignorespaces#1}%
    }%
}%

\newcommand*{\ShowNameAndAddress}{%
    \par\noindent\textbf{Name:}~\MandatoryName
    \IfNoText{\OptionalAddress}{}{%
        \par\noindent\textbf{Address:}~\OptionalAddress
    }%
}%
\begin{document}
\section{Name with no Address}
\SetName{Peter's Pizza}
\ShowNameAndAddress

\section{Name with Empty address}
\ifdefined\UseEgregsIfNoText
    % Want to be able to handle this:
    \SetAddress{


    }
\else
    \SetAddress{
    }
\fi

\ShowNameAndAddress


\section{Name with Address Given}

\SetAddress{
  123 Main Street,
  Anytown, USA
}

\ShowNameAndAddress

\section{Name with Address using Name}

%Verify: \verb|\MandatoryName =| \MandatoryName

\ifdefined\UseEgregsIfNoText
    % Want to be able to access value of \MandatoryName here
    \SetAddress{
      123 \MandatoryName Way,
      Anytown, USA
    }
\else
    % Can't even compile in this case with \MandatoryName 
    \SetAddress{
      123 %\MandatoryName Way,
      Anytown, USA
    }
\fi

\ShowNameAndAddress

\end{document}

I'm not saying this is the "proper" way. Just "a" way.

Werner
  • 603,163
  • I disagree with your comment regarding the fact that "definition only exists within \SetAddress" (although I do see that it is needed for your code to work). Try commenting out %\def\UseEgregsIfNoText{}% and you'll see that \OptionalAddress actually prints something for section 3. Perhaps you can add some text to explain this issue. Other than that I think you have improved on an @egreg solution, for which there should be a badge. :-) – Peter Grill Feb 18 '12 at 07:45
1

Here is another one:

\usepackage{ifthen}

\newcommand{\mycommand}[1]{%
\ifthenelse{\equal{#1}{}}{%
It is empty!%
}{%
It is not empty!%
}}
Kaveh
  • 1,177
  • 2
  • 10
  • 21
1

I want to show an alternative approach which is the result of @egreg, @JosephWright and me.

First of all I want to note that blank lines are a little bit strange, because a blank line sets everypar and this can be redefined.

Whatever here an approach using LaTeX3 in combination with xparse.

The argument typ m needs the prefix + to allow long arguments.

One special command is defined.

\prg_new_protected_conditional:Npnn \peter_if_blank_argument:N #1  {  T , F , TF }
{
   \hbox_set:Nn  \l_tmpa_box 
     {
      \tex_ignorespaces:D \tl_use:N #1 
     }
   \if_dim:w \box_wd:N \l_tmpa_box = \c_zero_dim 
    \prg_return_true:
   \else:
    \prg_return_false: 
   \fi:
 }

The function \prg_new_protected_conditional:Npnn allows us to define a new condition. The new function \peter_if_blank_argument:N saves the first argument inside a hbox and test whether the dim of the hbox is zero or not.

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\tl_new:N \l_peter_setname_tl
\tl_new:N \l_peter_setaddress_tl

\NewDocumentCommand { \SetName} { +m }
 {
  \tl_set:Nn \l_peter_setname_tl { #1 }
 }

\NewDocumentCommand { \SetAddress} { +m }
 {
  \tl_set:Nn \l_peter_setaddress_tl { #1 }
 }


\prg_new_protected_conditional:Npnn \peter_if_blank_argument:N #1  {  T , F , TF }
 {
    \hbox_set:Nn  \l_tmpa_box 
     {
        \tex_ignorespaces:D \tl_use:N #1 
      }
   \if_dim:w    \box_wd:N \l_tmpa_box =    \c_zero_dim 
         \prg_return_true:
   \else:
          \prg_return_false: 
    \fi:
 }

\NewDocumentCommand { \ShowNameAndAddress } {  }
 {
  \peter_if_blank_argument:NF \l_peter_setname_tl 
      { \par \noindent  \textbf{Name:}~\tl_use:N  \l_peter_setname_tl }
  \peter_if_blank_argument:NF \l_peter_setaddress_tl 
      {
         \par \noindent  \textbf{Address:}~\tl_use:N \l_peter_setaddress_tl
       } 
  \tl_clear:N \l_peter_setname_tl
  \tl_clear:N \l_peter_setaddress_tl
 }

 \ExplSyntaxOff

\begin{document}
\section{Name with no Address}
\SetName{Peter's Pizza}
\SetAddress{


}
\ShowNameAndAddress

\end{document}
Marco Daniel
  • 95,681
  • One of these days I will try to learn this syntax. But having problem adapting this to the last test case from my MWE where \MandatoryName is used in specifying the address. – Peter Grill Feb 18 '12 at 16:29
  • @PeterGrill: We are taking about such code in the chat. Hope to see you there ;-) – Marco Daniel Feb 18 '12 at 17:46