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 am trying to dynamically define variables. What I want to do is be able to define a person in the following way \definePerson{MrTestKey}{Mr Test}{0400 000 000}{mr@test.com} and then subsequently call \nameMrTestKey to return "Mr Test", \phoneMrTestKey to return "0400 000 000" and \emailMrTestKey to return "mr@test.com".

This is what I have tried so far... I use the \defineVariable macro so that I can call \nameMrTestKey to return a formatted (\emph) name and \nameMrTestKey* to return an unformatted name.

% To allow (un)formatted variables
% See http://tex.stackexchange.com/questions/51652/remove-formatting/
\usepackage{xparse}
\NewDocumentCommand\defineVariable{mmm}{%
    \NewDocumentCommand#1{s}{\IfBooleanTF{##1}{#3}{#2{#3}}}%
}

% \definePerson{key}{name}{phone}{email}
\newcommand\definePerson[4]{%
    \defineVariable\name#1{\emph}{#2}
    \defineVariable\phone#1{\emph}{#3}%
    \defineVariable\email#1{\emph}{#4}%
}
share|improve this question
    
Why not use a key value syntax instead? \definePerson[MrTest][name={Mr Test}, phone={...}, email={...}]. For multiple arguments like this, IMHO a key value syntax is easier to remember than the order of the arguments. –  Aditya Jul 20 '12 at 13:56

3 Answers 3

up vote 8 down vote accepted

You need to use \csname ... \endcsname to create a control sequence with the desired name. So the following creates a macro named \nameMrTestKey with the value being \emph{Mr Test} if called with \definePerson{MrTestKey}{Mr Test}...

\expandafter\newcommand\csname name#1\endcsname{\emph{#2}}

The name in the control sequence name is hard coded, and MrTestKey is the first parameter passed to \definePerson.

The \expandafter is required as the \newcommand needs to be dealt with after the \csname. See follow-up question: How to know whether or not we need \expandafter?

enter image description here

Code:

\documentclass{article}

\newcommand\definePerson[4]{%
    \expandafter\newcommand\csname name#1\endcsname{\emph{#2}}
    \expandafter\newcommand\csname phone#1\endcsname{\emph{#3}}
    \expandafter\newcommand\csname email#1\endcsname{\emph{#4}}
}

\begin{document}

\definePerson{MrTestKey}{Mr Test}{0400 000 000}{mr@test.com}

Name is \nameMrTestKey 

Phone is \phoneMrTestKey 

Email address is \emailMrTestKey 
\end{document}

Or expanding on your MWE:

enter image description here

\documentclass{article}

\usepackage{xparse}
\NewDocumentCommand\defineVariable{mmmm}{%
    \global\expandafter\NewDocumentCommand\csname#1#2\endcsname{s}{\IfBooleanTF{##1}{#4}{#3{#4}}}%
}


\newcommand\definePerson[4]{%
    \defineVariable{name}{#1}{\emph}{#2}
    \defineVariable{phone}{#1}{\emph}{#3}
    \defineVariable{email}{#1}{\emph}{#4}
}

\begin{document}

\definePerson{MrTestKey}{Mr Test}{0400 000 000}{mr@test.com}

Name is \nameMrTestKey\ or \nameMrTestKey*

Phone is \phoneMrTestKey\ or \phoneMrTestKey*

Email address is \emailMrTestKey\ or \emailMrTestKey*
\end{document}
share|improve this answer
    
Almost perfect. Thanks. In order to use my \defineVariable command... I did the following. \newcommand\definePerson[4]{% \expandafter\defineVariable\csname name#1\endcsname{\person}{#2} \expandafter\defineVariable\csname phone#1\endcsname{\phone}{#3} \expandafter\defineVariable\csname email#1\endcsname{\email}{#4} } –  Joshua Spence Jul 20 '12 at 5:58
2  
@JoshuaSpence: See updated solution which incorporates adapts your xparse version. –  Peter Grill Jul 20 '12 at 6:03
    
Dynamically calling also makes use of \csname: \csname phone#1\endcsname. –  koppor Dec 12 '13 at 21:05

I would take a slightly different tack and have a much tighter user interface. As Bruno answered in \NewDocumentCommand with constructed csname, the general feeling of the team is to try to have well defined interface commands which are not 'dynamically' generated.

At the same time, I would store the data in a 'proper' structure rather than using a \csname-based array (there are performance implications to the latter approach due to hash table collisions). The information required can be stored in a series of property lists:

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\prop_new:N \g__JS_name_prop
\prop_new:N \g__JS_telephone_prop
\prop_new:N \g__JS_email_prop
\NewDocumentCommand { \defineperson } { m m m m }
  {
    \prop_gput:Nnn \g__JS_name_prop {#1} {#2}
    \prop_gput:Nnn \g__JS_telephone_prop {#1} {#3}
    \prop_gput:Nnn \g__JS_email_prop {#1} {#4}
  }
\NewDocumentCommand { \getperson } { s m }
  {
    \IfBooleanTF #1
      { \emph }
      { \use:n }
      { \prop_get:Nn \g__JS_name_prop {#2} }
  }
\NewDocumentCommand { \gettelephone } { s m }
  {
    \IfBooleanTF #1
      { \emph }
      { \use:n }
      { \prop_get:Nn \g__JS_telephone_prop {#2} }
  }
\NewDocumentCommand { \getemail } { s m }
  {
    \IfBooleanTF #1
      { \emph }
      { \use:n }
      { \prop_get:Nn \g__JS_email_prop {#2} }
  }
\ExplSyntaxOff
\defineperson{MrTestKey}{Mr Test}{0400 000 000}{mr@test.com}
\begin{document}
\getperson*{MrTestKey} \getemail{MrTestKey}
\end{document}

(I have written code for a spreadsheet-like 'data tabe' variable type, but this is still experimental and probably not needed here.)

share|improve this answer

Here is a single macro that defines the necessary commands to use a starred/unstarred version of name, phone and email:

enter image description here

\documentclass{article}
\usepackage{xparse}% http://ctan.org/pkg/xparse
\makeatletter
\let\var@format\relax% Default formatting
\NewDocumentCommand\definePerson{mmmm}{%
  \expandafter\gdef\csname name@#1\endcsname{\var@format{#2}}%
  \expandafter\gdef\csname phone@#1\endcsname{\var@format{#3}}%
  \expandafter\gdef\csname email@#1\endcsname{\var@format{#4}}%
  \expandafter\NewDocumentCommand\expandafter{\csname name#1\endcsname}{s}{%
    {\IfBooleanTF{##1}{\let\var@format\emph}{}\csname name@#1\endcsname}}%
  \expandafter\NewDocumentCommand\expandafter{\csname phone#1\endcsname}{s}{%
    {\IfBooleanTF{##1}{\let\var@format\emph}{}\csname phone@#1\endcsname}}%
  \expandafter\NewDocumentCommand\expandafter{\csname email#1\endcsname}{s}{%
    {\IfBooleanTF{##1}{\let\var@format\emph}{}\csname email@#1\endcsname}}%
}
\makeatother
\begin{document}

\definePerson{MrTestKey}{Mr Test}{1-234-567-8901}{mr@test.com}
\definePerson{MrsTestKey}{Mrs Test}{1-234-567-8901}{mrs@test.com}
\nameMrTestKey\ \phoneMrTestKey*\ \emailMrTestKey \par
\nameMrsTestKey*\ \phoneMrsTestKey\ \emailMrsTestKey*

\end{document}

xparse is not really needed for this since the LaTeX \@ifstar can also be used to discern between starred variants.

share|improve this answer

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.