xparse and key value arguments

Here is a very elementary view on LaTeX's key-value system using keyval, similar to what was presented in How to create a command with key values?:

enter image description here

\documentclass{article}
\usepackage{keyval,xparse}% http://ctan.org/pkg/{keyval,xparse}

\makeatletter
% ========= KEY DEFINITIONS =========
\define@key{mymacro}{first}{\def\mm@first{#1}}
\define@key{mymacro}{second}{\def\mm@second{#1}}
\define@key{mymacro}{third}{\def\mm@third{#1}}
\define@key{mymacro}{last}{\def\mm@last{#1}}
\DeclareDocumentCommand{\myMacro}{m}{%
  \begingroup%
  % ========= KEY DEFAULTS + new ones =========
  \setkeys{mymacro}{first={FIRST arg},second={SECOND arg},third={THIRD arg},last={LAST arg},#1}%
  First arg: \mm@first \par
  Second arg: \mm@second \par
  Third arg: \mm@third \par
  Last arg: \mm@last
  \endgroup%
}
\makeatother

\begin{document}

\myMacro{last=LaSt,first=FiRsT} \par \hrulefill

\myMacro{} \par \hrulefill

\myMacro{third={$x^2$ \textbf{stuff}},second={}}
\end{document}

It should be evident that using a key-value approach removes the requirement to remember the order of the keys. That is, unless the keys have some interaction with one another.

Grouping (via \begingroup...\endgroup) localizes the changes to the keys. However, you can make things available globally. It just depends on your application. Moreover, it would be possible to adapt the key-values to actually create key-macros that you can re-use. This may be helpful if you have a area in your document where you perform a bunch of declarations, and want to re-use them later.

The above example translates just as well to document environments.

More extensions are provided by xkeyval, ltxkeys and even l3keys from expl3 (see Programming key–value in expl3).


Here's a practical example of using key-values to store data regarding movies through a movie declaration (\newMovie{<tag>}{<key-values>}) and printing it (\showMovie{<tag>}):

enter image description here

\documentclass{article}
\usepackage{keyval,xparse,url}% http://ctan.org/pkg/{keyval,xparse,url}

\makeatletter
% ========= KEY DEFINITIONS =========
\define@key{movie}{title}{\expandafter\def\csname \movietag @title\endcsname{#1}}
\define@key{movie}{releaseyear}{\expandafter\def\csname \movietag @releaseyear\endcsname{#1}}
\define@key{movie}{genre}{\expandafter\def\csname \movietag @genre\endcsname{#1}}
\define@key{movie}{url}{\expandafter\def\csname \movietag @url\endcsname{#1}}
\DeclareDocumentCommand{\newMovie}{m m}{%
  % ========= KEY DEFAULTS + new ones =========
  \def\movietag{#1}% Store movie tag (used when setting/storing keys)
  \setkeys{movie}{title={},releaseyear={},genre={},url={},#2}% Create keys
}
\DeclareDocumentCommand{\showMovie}{m}{%
  \textbf{\csname #1@title\endcsname} (\csname #1@releaseyear\endcsname),
  \textit{\csname #1@genre\endcsname},
  \expandafter\expandafter\expandafter\url\expandafter\expandafter\expandafter{\csname #1@url\endcsname}.
}
\makeatother

\begin{document}

% Movie declarations
\newMovie{anchorman2}{
  url={http://www.imdb.com/title/tt1229340/},
  releaseyear=2013,
  title={Anchorman: The Legend Continues},
  genre=Comedy}
\newMovie{lego}{
  title={The Lego Movie},
  releaseyear=2014,
  url={http://www.imdb.com/title/tt1490017/},
  genre={Animation, Action, Comedy}}
\newMovie{magic}{
  title={Now You See Me},
  url={http://www.imdb.com/title/tt1670345/},
  releaseyear=2013,
  genre={Crime, Thriller}}

% Show movie details
\showMovie{magic}

\showMovie{anchorman2}

\showMovie{lego}

\end{document}

There are other (perhaps better) ways of dealing with this, but again, this is just to showcase what you can do with key-value approaches to macros (and databases).


An implementation entirely in expl3, using key-value pairs and property lists.

Each \newMovie command defines a property list and stores the values. Then \showMovie displays them in the preferred order.

\documentclass{article}
\usepackage{xparse,url}

\ExplSyntaxOn
% keys
\keys_define:nn { oxinabox/movies }
 {
  title .tl_set:N = \l_oxinabox_title_tl,
  releaseyear .tl_set:N = \l_oxinabox_releaseyear_tl,
  genre .tl_set:N = \l_oxinabox_genre_tl,
  url .tl_set:N = \l_oxinabox_url_tl,
 }

% user level commands
\NewDocumentCommand{\newMovie}{m m}
 {
  \oxinabox_newmovie:nn { #1 } { #2 }
 }

\NewDocumentCommand{\showMovie}{m}
 {
  \oxinabox_showmovie:n { #1 }
 }

% internal functions
\cs_new_protected:Npn \oxinabox_newmovie:nn #1 #2
 {
  \group_begin: % keep the assignment to the keys local
  \prop_new:c { g_oxinabox_movie_#1_prop }
  \keys_set:nn { oxinabox/movies } { #2 }
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { title } \l_oxinabox_title_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { releaseyear } \l_oxinabox_releaseyear_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { genre } \l_oxinabox_genre_tl
  \prop_gput:cnV { g_oxinabox_movie_#1_prop } { url } \l_oxinabox_url_tl
  \group_end:
 }

\cs_new:Npn \oxinabox_getvalue:nn #1 #2
 {
  \prop_item:cn { g_oxinabox_movie_#1_prop } { #2 }
 }
\cs_new_protected:Npn \oxinabox_showmovie:n #1
 {
  \par\noindent
  \textbf{ \oxinabox_getvalue:nn { #1 } { title } }, ~ %
  \textbf{ \oxinabox_getvalue:nn { #1 } { releaseyear } }, ~ %
  \textbf{ \oxinabox_getvalue:nn { #1 } { genre } },
  \\
  \use:x { \exp_not:N \url { \oxinabox_getvalue:nn { #1 } { url } } }
}
\ExplSyntaxOff
\begin{document}

% Movie declarations
\newMovie{anchorman2}{
  url={http://www.imdb.com/title/tt1229340/},
  releaseyear=2013,
  title={Anchorman: The Legend Continues},
  genre=Comedy}
\newMovie{lego}{
  title={The Lego Movie},
  releaseyear=2014,
  url={http://www.imdb.com/title/tt1490017/},
  genre={Animation, Action, Comedy}}
\newMovie{magic}{
  title={Now You See Me},
  url={http://www.imdb.com/title/tt1670345/},
  releaseyear=2013,
  genre={Crime, Thriller}}

% Show movie details
\showMovie{magic}

\showMovie{anchorman2}

\showMovie{lego}

\end{document}

Thanks to Werner for the data. ;-)

enter image description here