5

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.}
    \somemacro{01}{This is information about space.}

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?

1 Answer 1

5

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

Now with your input

\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.

1
  • 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. Jan 27, 2012 at 16:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .