5

I am trying to create a method MakeCommands which will generate a couple of commands based on the basic name passed as a parameter. However, I want the user to be allowed to pass a name with spaces, which therefore must be extracted from the passed parameter for use as actual function names

What I currently have (creating just one command, for simplicity's sake) is:

\newcommand*{\MakeCommand}[1]
{
    \expandafter\newcommand\csname the#1\endcsname{TEST}
}

This works, in that a call such as \MakeCommand{Bday} creates the command \theBday which expands to TEST. However, if the parameter is actually multiple words (\MakeCommand{Date of Birth}), then created command would have to be called using \csname theDate of Birth\endcsname, which isn't very elegant.

So I tried the following:

% \usepackage{xstring}
\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}{\StrSubstitute{#1}{ }{}}
    \expandafter\newcommand\csname the\temp\endcsname{TEST}
}

Basically, use xstring's StrSubstitute command to extract the spaces from the passed parameter and store it in \temp. And then use this spaceless version of the parameter as the command name.

This, however, doesn't work and I get multiple errors. For instance, for the following MWE:

\documentclass[a4paper,10pt]{article}
\usepackage{xstring}

\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}{\StrSubstitute{#1}{ }{}}
    \expandafter\newcommand\csname the\temp \endcsname{TEST}
    %\expandafter\newcommand\csname the#1\endcsname{TEST}
}
\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}

I get the following errors:

Missing \endcsname inserted. \MakeCommand{Date of Birth}
Command \the already defined. \MakeCommand{Date of Birth}
Extra \endcsname. \MakeCommand{Date of Birth}
Missing \begin{document}. \MakeCommand{Date of Birth}
Undefined control sequence. ^^I\theDateofBirth

If I comment out the \expandafter... line and replace \theDateofBirth inside the document contents with \temp, the file compiles just fine and outputs "DateofBirth", as expected.

I can tell from other readings ([A], [B], [C]) that this probably requires additional \expandafters, but I can't for the life of me figure out where to put them and why/how.


This is for a file which will display multiple data points. MakeCommand should actually create three related commands. What I meant to do was

\newcommand*{\temp}{}
\newcommand*{\MakeCommand}[1]
{
    \renewcommand*{\temp}
    {
        % store spaceless version of parameter
        \StrSubstitute{#1}{ }{}
    }
    \expandafter\newcommand\csname the\temp\endcsname{}% empty variable for use further ahead
    \expandafter\newcommand\csname \temp\endcsname[1]
    {
        % store value of data point
        \expandafter\renewcommand\csname the\temp \endcsname{#1}
    }
    \expandafter\newcommand\csname print\temp\endcsname
    {
        % print out formatted data point
        \textbf{\temp:} \csname\the\temp\endcsname
    }
}

So, MakeCommands{Date of Birth} would create three commands

  • \theDateofBirth, which is initially empty
  • \DateofBirth, which the user can later call to define \theDateofBirth
  • \printDateofBirth, which prints out the data point

For example, \DateofBirth{August 31, 2016} would cause \printDateofBirth to output "Date of Birth: August 31, 2016".

  • 1
    Why not just supply the name without spaces? – Werner Aug 31 '16 at 5:21
  • Can you maybe provide an example of why this would be helpful? (or is it for fun?) Why not simply do something like \makeatletter \newcommand\xxx[1]{\@nameuse{the#1}} \makeatother then use it like \xxx{Date of Birth}? (Not that I'd do this in one of my own documents.) – jon Aug 31 '16 at 5:59
  • @Werner: Woops. I was going to add that as a note at the end of my question but forgot. I've added to the question now. – Wasabi Aug 31 '16 at 11:00
4

If all you want to remove are spaces, the kernel function \zap@space is what you need:

\documentclass[a4paper,10pt]{article}

\makeatletter
\newcommand*{\MakeCommand}[1]{%
  \expandafter\newcommand\csname the\zap@space #1 \@empty\endcsname
}
\makeatother
\MakeCommand{Date of Birth}{TEST}

\begin{document}

\theDateofBirth

\end{document}

I have changed the syntax for \MakeCommand to allow specifying the replacement text or it would be pretty useless.

However, the the prefix is not really well chosen.

The (slower) implementation with \StrSubstitute:

\documentclass[a4paper,10pt]{article}
\usepackage{xstring}

\newcommand*{\MakeCommand}[1]{%
  \StrSubstitute{#1}{ }{}[\temp]%
  \expandafter\newcommand\csname the\temp\endcsname
}

\MakeCommand{Date of Birth}{TEST}

\begin{document}

\theDateofBirth

\end{document}
2

Here's a solution that makes use of expandable replacements, related to this answer. This adds a whole system of expandable replacements, so it's not just about spaces. Once you do

\setfreplace{removespaces}{ }{}

you can say \freplace{removespaces}{Date of Birth} and it will substitute every space into nothing (i.e., removing the spaces) leaving DateofBirth expandably.

Altough may be you shouldn't use \the... in the names, as that naming convention is used in LaTeX for other things. May be \TheDateofBirth, \showDateofBirth or \printDateofBirth.

Here is an expl3 solution

\documentclass{scrartcl}
\usepackage{xparse,etoolbox}

\ExplSyntaxOn

\cs_generate_variant:Nn \cs_generate_variant:Nn { c }

\NewDocumentCommand \setfreplace { m +m +m }
 {
  \freplace_set:nnn { #1 } { #2 } { #3 }
 }
\DeclareExpandableDocumentCommand \freplace { +m +m }
 {
  \freplace:nn { #1 } { #2 }
 }
\quark_new:N \q_freplace
\cs_new:Npn \freplace:nn #1 #2
 {
  \exp_not:f { \use:c { freplace_#1:n } { #2 } }
 }
\cs_new_protected:Npn \freplace_set:nnn #1 #2 #3
 {
  \cs_set:cpx { freplace_#1:n } ##1
   {
    \exp_not:c { freplace_#1_auxi:nw } { } ##1 { \exp_not:N \q_freplace }
   }
  \cs_set:cpx { freplace_#1_auxi:nw } ##1 ##2 ##
   {
    \exp_not:c { freplace_#1_nobraces:nfn }
     { ##1 } { \exp_not:c { freplace_#1_do:n } { ##2 } }
   }
  \cs_set:cpx { freplace_#1_nobraces:nnn } ##1 ##2
   {
    \exp_not:c { freplace_#1_auxii:nn } { ##1 ##2 }
   }
  \cs_generate_variant:cn { freplace_#1_nobraces:nnn } { nf }
  \cs_set:cpx { freplace_#1_auxii:nn } ##1 ##2
   {
    \exp_not:N \str_if_eq:nnTF { \exp_not:N \q_freplace } { ##2 }
     { \exp_stop_f: ##1 }
     {
      \exp_not:c { freplace_#1_addbraces:nfw }
       { ##1 } { \exp_not:c { freplace_#1:n } { ##2 } }
     }
   }
  \cs_set:cpx { freplace_#1_addbraces:nnw } ##1 ##2
   {
    \exp_not:c { freplace_#1_auxi:nw } { ##1 { ##2 } }
   }
  \cs_generate_variant:cn { freplace_#1_addbraces:nnw } { nf }
  \cs_set:cpx { freplace_#1_do:n } ##1
   {
    \exp_not:N \tl_if_empty:nTF { ##1 }
     { \exp_stop_f: }
     {
      \exp_not:c { freplace_#1_auxiii:nww }
       { } ##1 \exp_not:n { #2 \q_freplace \q_stop }
     }
   }
  \cs_set:cpx { freplace_#1_auxiii:nww } ##1 ##2 #2 ##3 \q_stop
   {
    \exp_not:N \str_if_eq:nnTF { \exp_not:N \q_freplace } { ##3 }
     { \exp_stop_f: ##1 ##2 }
     {
      \exp_not:c { freplace_#1_auxiii:nww }
       { ##1 ##2 \exp_not:n { #3 } } ##3 \exp_not:N \q_stop
     }
   }
 }

\cs_generate_variant:Nn \freplace:nn { nV }
\cs_new_protected:Npn \tl_replace_nested:Nnn #1 #2 #3
 {
  \freplace_set:nnn { _tmp_ } { #2 } { #3 }
  \tl_set:Nx #1 { \freplace:nV { _tmp_ } #1 }
 }

\ExplSyntaxOff

\setfreplace{removespaces}{ }{}

\newcommand*{\MakeCommand}[1]
{\expandafter\newcommand\csname the\freplace{removespaces}{#1}\endcsname{TEST}}

\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}

After the update, here's a full command (I use commands from etoolbox to ease the code, which has expansion in account)

\newcommand*\temp{}
\newcommand*\MakeCommand[1]
 {\edef\temp{\freplace{removespaces}{#1}}%
  \csdef{the\temp}{}%
  \csedef{\temp}##1{\csdef{the\temp}{##1}}%
  \csedef{print\temp}{\unexpanded{\textbf{#1:}} \noexpand\csuse{the\temp}}}

And here another solution, a little bit less robust, without expl3 (in case you fear it and don't want to use it for some reason)

\documentclass{scrartcl}
\usepackage{etoolbox}

\long\def\usefirst#1#2{#1}
\long\def\usesecond#1#2{#2}
\def\strcmp#1#2{\ifnum\pdfstrcmp{\unexpanded{#1}}{\unexpanded{#2}}=0 \expandafter\usefirst\else\expandafter\usesecond\fi}
\def\strempty#1{\if\relax\detokenize{#1}\relax\expandafter\usefirst\else\expandafter\usesecond\fi}

\def\endreplace{\endreplace}
\def\nestedreplace#1{\csuse{nestedreplace:#1}}
\def\newnestedreplace#1#2#3{%
  \csdef{nestedreplace:#1}##1{\csuse{nestedreplace:#1:A}##1{\endreplace}}%
  \csdef{nestedreplace:#1:A}##1##{\csuse{doreplacement:#1}{##1}\csuse{nestedreplace:#1:B}}%
  \csdef{nestedreplace:#1:B}##1{\strcmp{##1}{\endreplace}{}
    {{\csuse{nestedreplace:#1}{##1}}\csuse{nestedreplace:#1:A}}}%
  \csdef{doreplacement:#1}##1{\strempty{#1}{}{\csuse{doreplacement:#1:A}{}##1#2\endreplace\qend}}%
  \csdef{doreplacement:#1:A}##1##2#2##3\qend{\strcmp{\endreplace}{##3}
    {\unexpanded{##1##2}}{\csuse{doreplacement:#1:A}{##1##2#3}##3\qend}}%
}

\newnestedreplace{removespaces}{ }{}

\newcommand*{\MakeCommand}[1]
{\expandafter\newcommand\csname the\nestedreplace{removespaces}{#1}\endcsname{TEST}}

\MakeCommand{Date of Birth}

\begin{document}
    \theDateofBirth
\end{document}
  • Done, removed; it was there for no reason. – Manuel Aug 31 '16 at 6:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.