Nested \newcommand for creating maps

I want to create a simple map using \newcommand. My \NewMapItem command defines another command which returns item's value. \MapItem simply replaces to that command.

\documentclass{article}
\newcommand{\MapItem}[1]{\Map@#1}
\begin{document}
\NewMapItem{Canberk}{Qwerty}
\MapItem{Canberk}
\end{document}


But it doesn't works. Output is:

line 5: Missing number, treated as zero. \NewMapItem{Canberk}{Qwerty}
line 5: You already have nine parameters. \NewMapItem{Canberk}{Qwerty}


Could you explain?

1 Answer

See Latex dynamic macro definition for the \NewMapItem problem and also How to know whether or not we need \expandafter? for a discussion about the matter.

\newcommand{\NewMapItem}[2]{%
\expandafter\newcommand\csname Map@#1\endcsname{#2}}
\newcommand{\MapItem}[1]{\csname Map@#1\endcsname}


You can't form a token in that way unless you use \csname.

A different solution with expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% allocate a global property list
\prop_new:N \g_jnbrq_maps_prop

% the user level command for adding properties
\NewDocumentCommand{\NewMapItem}{mm}
{
\prop_gput:Nnn \g_jnbrq_maps_prop { #1 } { #2 }
}
% the user level command for retrieving a property
\DeclareExpandableDocumentCommand{\MapItem}{m}
{
\prop_item:Nn \g_jnbrq_maps_prop { #1 }
}
\ExplSyntaxOff

\begin{document}

\NewMapItem{Canberk}{Qwerty}

\MapItem{Canberk}

\end{document}


It's possible to check whether the property already exists:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% allocate a global property list
\prop_new:N \g_jnbrq_maps_prop
% an error message for duplicate property
\msg_new:nnnn { jnbrq/maps } { duplicate-property }
{
Already~existing~property~#1
}
{
Property~#1~is~already~defined,~I'm~ignoring~
this~redefinition
}

% the user level command for adding properties
\NewDocumentCommand{\NewMapItem}{mm}
{
\prop_if_in:NnTF \g_jnbrq_maps_prop { #1 }
{
\msg_error:nnn { jnbrq/maps } { duplicate-property } { #1 }
}
{
\prop_gput:Nnn \g_jnbrq_maps_prop { #1 } { #2 }
}
}
% the user level command for retrieving a property
\DeclareExpandableDocumentCommand{\MapItem}{m}
{
\prop_item:Nn \g_jnbrq_maps_prop { #1 }
}
\ExplSyntaxOff

\begin{document}
\NewMapItem{Canberk}{Qwerty}
\NewMapItem{Canberk}{QQQ}

\MapItem{Canberk}
\end{document}


This will raise an error

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! jnbrq/maps error: "duplicate-property"
!
! Already existing property Canberk
!
! See the jnbrq/maps documentation for further information.
!
! For immediate help type H <return>.
!...............................................

l.37 \NewMapItem{Canberk}{QQQ}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| Property Canberk is already defined, I'm ignoring this redefinition
|...............................................


which would be more easily understood instead of the standard error issued by \newcommand.

• @wipet Because \newcommand checks for duplicate definitions, while \def doesn't. – egreg Apr 17 '15 at 19:47
• But the \csname Map@soemthing\endcsname cannot be duplicate. If this is not true then you have chosen bad prefix Map@. And when user type \NewMapItem{the same as before} then it is clear that user needs to re-define it. – wipet Apr 17 '15 at 19:51
• @wipet I don't think so, judging from the original code. – egreg Apr 17 '15 at 19:57