# Making and referring to variables with a key

I have a custom macro, \somemacro{}{}. In the macro, #1 is set to a unique key (e.g. 11) and #2 contains other data. This macro appears throughout my document:

    This is some text. \somemacro{11}{This is information about animals.}
\somemacro{15}{This is information about trees.} Here is some other text.
\chapter{Here is a new chapter}
\somemacro{17}{This is information about the sky.}


Occasionally, I need to cross-reference the appearance of the macros, by referring to the key number. This is similar to \ref or \pageref, but the value will be a number referring to the order of appearance of that key. E.g. In the above, #15 appears second, so \whereiskey{15} would return 2. #01 appears fourth in the document, so \whereiskey{01} would return 4.

To do this, I have created a counter labelcounter, which is incremented each time the macro is used. To create a variable that has a key and a value, I tried to create use a counter (as it is the only variable I know that has a name and a value), named for the value of the key:

\documentclass{article}
\newcounter{labelcounter}
\setcounter{labelcounter}{0}
\newcommand{\somemacro}[2]{
\stepcounter{labelcounter}
\newcounter{#1}
\setcounter{labelcounter}
#2
}
\newcommand{\whereiskey}[1]{
\arabic{#1}
}
\begin{document}
\somemacro{08}{This is some text.}
\somemacro{12}{This is some other text.}
\somemacro{10}{This is some more text.}
This number should say when \#12 appears: \whereiskey{10}.
\end{document}


This does not seem to work. When I try to compile, it reports,

Missing number, treated as zero. \somemacro{08}{This is some text.}.

• How can I get this to work?
• Is there a better approach?
-

You're not specifying to what value the counter labelcounter should be set to.

However, defining a new counter for every instance of \somemacro is definitely a bad idea. Besides, \arabic{#1} will always return 0.

If keys are unique, you should refer to those.

\newcounter{keycounter}
\makeatletter
\newcommand{\somemacro}[2]{%
\@ifundefined{keyappeared@#1}
{\stepcounter{keycounter}%
\expandafter\xdef\csname keyappeared@#1\endcsname{\the\c@keycounter}%
}{}%
#2}
\newcommand{\whereiskey}[1]{%
\@ifundefined{keyappeared@#1}
{wrong or not yet appeared key''}
{\@nameuse{keyappeared@#1}}}
\makeatother


\somemacro{08}{This is some text.}
\somemacro{12}{This is some other text.}
\somemacro{10}{This is some more text.}
This number should say when \#10 appears: \whereiskey{10}.


you'll get 3 from \whereiskey{10}. If the same key appears twice, it won't increment keycounter.

You can also use directly the \label-\ref system:

\newcounter{keycounter}
\makeatletter
\newcommand{\somemacro}[2]{%
\@ifundefined{keyappeared@#1}
{\refstepcounter{keycounter}\label{keyappeared@#1}%
\global\expandafter\let\csname keyappeared@#1\endcsname\@empty
}{}%
#2}
\newcommand{\whereiskey}[1]{%
\@ifundefined{keyappeared@#1}
{\typeout{Possibly wrong key, run LaTeX again}}{}%
\ref{keyappeared@#1}%
}
\makeatother


This frees you from the constraint that a \whereiskey command must appear after the corresponding \somemacro.

-
The revised method is a good idea, but I can't get the it to work after repeated compiles. Yields a blank for keys that appear after the are referenced. –  Peter Grill Jan 27 '12 at 16:22
@PeterGrill Thanks: misplaced braces! –  egreg Jan 27 '12 at 16:37