7

I would like to have a macro \uniqueID{...} that causes a message in the console, or better, a crash (with a message in the console) if it has been called two times with the same argument during the compilation of document.

How to do such a macro with (La)TeX?


Edit

If the macro is called two times with the empty argument, there should be no warning

  • 1
    You should not add further requests, however ;-) – user31729 Apr 18 '18 at 19:28
  • Yes you are right. But I guess I would be able to manage this last one :) I really don't understand why I am able to code and like that but why I am so reluctant to learn TeX. It feels so complicated!!! I often dream of a massive move to something like Python+LaTeX with a good IDE... – Colas Apr 18 '18 at 19:32
  • Not necessary, in my case ... I don't like IDEs ... they hide too much. (and I don't know too much about TeX, however) – user31729 Apr 18 '18 at 19:34
  • But you can't say that \seq_if_in:NnTF does not just look like... assembly language or some esoteric language from the 70s... Anyway: THANKS! – Colas Apr 18 '18 at 19:37
  • Well, if \seq_if_in is too esoteric for you, my solution was not really useful to you. Deleted it... – user31729 Apr 18 '18 at 19:55
7

For each call to \uniqueID{<id>}, store a control sequence using <id>. That way you can check whether that <id> already exists and generate an error accordingly:

\documentclass{article}

\makeatletter
\newcommand{\uniqueID}[1]{%
  % First see if #1 is empty (https://tex.stackexchange.com/a/53091/5764)
  \if\relax\detokenize{#1}\relax\else
    \ifcsname ID-#1\endcsname % Check if ID already exists
      \@latex@error{ID #1 already used}{}
    \fi
  \fi
  \expandafter\def\csname ID-#1\endcsname{}% Create ID
  #1% <process ID>
}
\makeatother

\begin{document}

\uniqueID{} % empty

\uniqueID{foo}

\uniqueID{} % empty

\uniqueID{bar}

\uniqueID{foo} % error

\end{document}
  • 1
    I would use ID:\detokenize{#1} to be on the secure side. – Manuel Apr 18 '18 at 20:15
  • 1
    I wouldn't use : (try with babel-french). – egreg Apr 18 '18 at 21:33
  • @egreg: Good point... – Werner Apr 18 '18 at 21:35
  • @Werner Wouldn't it be better with \gdef instead of \def? – Colas May 13 '18 at 20:06
  • @Colas: Only if you're typically placing \uniqueID{..} within groups (environments) and want it to span across that. – Werner May 14 '18 at 16:58
5

An approach using listofitems. A concatenated list of arguments to the macro is created, and then searched for duplication. I provide verbose output (which can be removed) to show how it works.

\documentclass{article}
\usepackage{listofitems}
\def\usedIds{}
\makeatletter
\newcommand\uniqueId[1]{%
  \g@addto@macro\usedIds{#1\endlinechar}%
  \setsepchar{#1\endlinechar}%
  \readlist\IDlist{\usedIds}%
  \ifnum\listlen\IDlist[]>2\relax%
    \typeout{BOOM! #1 is a duplicate.}BOOM! #1 is a duplicate.
  \else
    #1 OK.
  \fi%
}
\makeatother
\begin{document}
\uniqueId{foo}

\uniqueId{foobar}

\uniqueId{foo}

\uniqueId{foobar}

\uniqueId{foobar}

\end{document}

enter image description here

Here is a non-verbose version that types out to the console and then triggers an error.

\documentclass{article}
\usepackage{listofitems}
\def\usedIds{}
\makeatletter
\newcommand\uniqueId[1]{%
  \g@addto@macro\usedIds{#1\endlinechar}%
  \setsepchar{#1\endlinechar}%
  \readlist\IDlist{\usedIds}%
  \ifnum\listlen\IDlist[]>2\relax%
    \typeout{BOOM! #1 is a duplicate.}
    Abort \uniqueIdHasBeenTriggered
  \fi%
}
\makeatother
\begin{document}
\uniqueId{foo}

\uniqueId{foobar}

\uniqueId{foo}

\uniqueId{foobar}

\uniqueId{foobar}

\end{document}

The console is this:

This is pdfTeX, Version 3.14159265-2.6-1.40.18 (MiKTeX 2.9.6350)
entering extended mode
(C:/steven.segletes/TeX/Working/junk.tex
LaTeX2e <2017-04-15>
Babel <3.10> and hyphenation patterns for 72 language(s) loaded.
(C:\MikTeX\tex\latex\base\article.cls
Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
(C:\MikTeX\tex\latex\base\size10.clo))
(C:\MikTeX\tex\latex\listofitems\listofitems.sty
(C:\MikTeX\tex\generic\listofitems\listofitems.tex)) (junk.aux)
BOOM! foo is a duplicate.
! Undefined control sequence.
\uniqueId ...te.} Abort \uniqueIdHasBeenTriggered 
                                                  \fi 
l.20 \uniqueId{foo}

? 
Process interrupted by user
4

Here's an expl3 implementation:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\uniqueID}{m}
 {
  \tl_if_blank:nF { #1 }
   {
    \prop_if_in:NnTF \g_colas_uniqueid_prop { #1 }
     {
      \msg_error:nnn { colas } { uniqueID } { #1 }
     }
     {
      \prop_gput:Nnn \g_colas_uniqueid_prop { #1 } { }
     }
   }
 }
\prop_new:N \g_colas_uniqueid_prop
\msg_new:nnnn { colas } { uniqueID }
 {
  Already~used~ID~#1
 }
 {
  The~ID~#1~has~already~been~used.~Too~bad.
 } 
\ExplSyntaxOff

\begin{document}

A \uniqueID{A}

Empty \uniqueID{}

Blank \uniqueID{ }

A \uniqueID{A}

Empty \uniqueID{}

\end{document}

Of course you should add code to be executed when the ID is blank or new.

Here's the console output:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! colas error: "uniqueID"
! 
! Already used ID A
! 
! See the colas documentation for further information.
! 
! For immediate help type H <return>.
!...............................................  

l.36 A \uniqueID{A}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| The ID A has already been used. Too bad.
|...............................................
? 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.