# A macro that detects if the same argument has been given two times

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

• 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

## 3 Answers

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}

• I would use ID:\detokenize{#1} to be on the secure side. – Manuel Apr 18 '18 at 20:15
• 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

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}


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


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