6

I'd like to create a command to print some contacts inserted with same 3 commands: \name{},\work{},\email{}.

Every time we use those 3 commands, a new contact is collected and then all of them is printed as below.

I suspect that I need some kind of array of list, to save the contacts, but I have no idea how to search for this.

MWE

\documentclass{article}

\makeatletter
\def\listcontacts{%
\noindent\textbf{\@name}\par
\noindent\textit{\@work}\par
\noindent\texttt{\@email}\par

\vskip 1in % to start a new block of contact
}

\newcommand{\name}[1]{\def\@name{#1}}
\newcommand{\work}[1]{\def\@work{#1}}
\newcommand{\email}[1]{\def\@email{#1}}

\newcommand{\information}[3]{%
\name{#1}
\work{#2}
\email{#3}
}
\makeatother

\information{Faa Foo}{Univ.\ Blah}{goo@goo.com}


%% I'd like to insert a new one with same commands
%\name{Faa Foo}
%\work{Univ.\ Blah}
%\email{goo@goo.com}


% and repeat the process with different number of blocks, 
%that is, could be any finite number of contacts.

\begin{document}
\listcontacts
\end{document}
Sigur
  • 37,330

4 Answers4

6

I have made it so that \@name[<index>] \@work[<index>], and \@email[<index>] are arrays, where the index is stepped with every call to \information{}{}{}.

\listcontact{<index>} will provide the contact info for the one specified index. In the MWE, I loop through all indices.

\documentclass{article}
\usepackage{pgffor}
\makeatletter
\newcounter{infocnt}
\newcommand{\name}[1]{%
  \expandafter\def\csname @name[\theinfocnt]\endcsname{#1}}
\newcommand{\work}[1]{%
  \expandafter\def\csname @work[\theinfocnt]\endcsname{#1}}
\newcommand{\email}[1]{%
  \expandafter\def\csname @email[\theinfocnt]\endcsname{#1}}
\newcommand{\information}[3]{%
  \stepcounter{infocnt}%
  \name{#1}%
  \work{#2}%
  \email{#3}%
}
\newcommand\listcontact[1]{\noindent%
  NAME: \textbf{\csname @name[#1]\endcsname}\\
  WORK: \textbf{\csname @work[#1]\endcsname}\\
  EMAIL: \textbf{\csname @email[#1]\endcsname}\par
  \vskip 1in
}
\makeatother
\information{Faa Foo}{Univ.\ Blah}{goo@goo.com}
\information{XXX}{YYY}{ZZZ@goo.com}
\begin{document}
\foreach\x in {1,2,...,\theinfocnt}{\listcontact{\x}}
\end{document}

enter image description here

If I had my 'druthers, though, I would input the list all at once, quite simply, with the same output. The listofitems package immediately stores the list as an accessible array:

\documentclass{article}
\usepackage{listofitems}
\newcommand\listcontact[1]{\noindent%
  NAME: \textbf{\contacts[#1,1]}\\
  WORK: \textbf{\contacts[#1,2]}\\
  EMAIL: \textbf{\contacts[#1,3]}\par
  \vskip 1in
}
\setsepchar{\\/&}
\begin{document}
\readlist*\contacts{
 Faa Foo & Univ.\ Blah & goo@goo.com\\
 XXX & YYY & ZZZ@goo.com}
\foreachitem\x\in\contacts[]{\listcontact{\xcnt}}

Here is the email of contact 2: \contacts[2,3].
\end{document}

enter image description here

With a small supplement from the readarray package, the list of contacts could even be stored in an external file.

4

This is “property lists” playground. Here I define \newcontact for defining a contact with a key-value interface.

With \newcontactscheme one defines different ways to print the data; default is used by \listcontact if no optional argument is supplied. In the definition, use \getcontact{<key>}{#1} to print the value corresponding to <key> for the current contact, represented by #1.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\newcontact}{mm}
 {% #1 = contact label, #2 = data
  \prop_new:c { g_sigur_contact_#1_prop }
  \prop_gset_from_keyval:cn { g_sigur_contact_#1_prop } { #2 }
 }
\NewDocumentCommand{\listcontact}{O{default}m}
 {
  \cs_set_eq:Nc \__sigur_contact_list:n { sigur_contact_list_#1:n }
  \__sigur_contact_list:n { #2 }
 }
\NewDocumentCommand{\getcontact}{mm}
 {
  \sigur_contact_print:nn { #1 } { #2 }
 }
\NewDocumentCommand{\newcontactscheme}{m+m}
 {% #1 = scheme name, #2 = definition
  \cs_new_protected:cn { sigur_contact_list_#1:n } { #2 }
 }
\cs_new_protected:Nn \sigur_contact_print:nn
 {
  \prop_item:cn { g_sigur_contact_#2_prop } { #1 }
 }
\ExplSyntaxOff

\newcontactscheme{default}{%
  \par\noindent
  \getcontact{name}{#1}\\
  \getcontact{work}{#1}\\
  \getcontact{email}{#1}\par
}
\newcontactscheme{short}{%
  \getcontact{name}{#1}, \getcontact{email}{#1}%
}
\newcontactscheme{alternate}{%
  \begin{tabular}[t]{@{}l@{ }l@{}}
  Name:  & \getcontact{name}{#1} \\
  Work:  & \getcontact{work}{#1} \\
  Email: & \texttt{\getcontact{email}{#1}}
  \end{tabular}%
}

\newcontact{foo}{
  name=Faa Foo,
  work=Univ.\ Blah,
  email=goo@goo.com
}
\newcontact{xxx}{
  name=XXX,
  work=YYY,
  email=ZZZ@goo.com
}

\begin{document}

\listcontact{foo}

\medskip

\listcontact{xxx}

\medskip

Short: \listcontact[short]{xxx}

\medskip

Alternate: \listcontact[alternate]{foo}

\end{document}

enter image description here

A version that also defines \listallcontacts (the optional argument is one of the defined schemes).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\newcontact}{mm}
 {% #1 = contact label, #2 = data
  \prop_new:c { g_sigur_contact_#1_prop }
  \prop_gset_from_keyval:cn { g_sigur_contact_#1_prop } { #2 }
  \seq_gput_right:Nn \g_sigur_contact_seq { #1 }
 }
\NewDocumentCommand{\listcontact}{O{default}m}
 {
  \cs_set_eq:Nc \__sigur_contact_list:n { sigur_contact_list_#1:n }
  \__sigur_contact_list:n { #2 }
 }
\NewDocumentCommand{\listallcontacts}{O{default}}
 {
  \cs_set_eq:Nc \__sigur_contact_list:n { sigur_contact_list_#1:n }
  \seq_map_inline:Nn \g_sigur_contact_seq
   {
    \__sigur_contact_list:n { ##1 } \par
   }
 }
\NewDocumentCommand{\getcontact}{mm}
 {
  \sigur_contact_print:nn { #1 } { #2 }
 }
\NewDocumentCommand{\newcontactscheme}{m+m}
 {% #1 = scheme name, #2 = definition
  \cs_new_protected:cn { sigur_contact_list_#1:n } { #2 }
 }

\seq_new:N \g_sigur_contact_seq

\cs_new_protected:Nn \sigur_contact_print:nn
 {
  \prop_item:cn { g_sigur_contact_#2_prop } { #1 }
 }
\ExplSyntaxOff

\newcontactscheme{default}{%
  \par\noindent
  \getcontact{name}{#1}\\
  \getcontact{work}{#1}\\
  \getcontact{email}{#1}\par
}
\newcontactscheme{short}{%
  \getcontact{name}{#1}, \getcontact{email}{#1}%
}
\newcontactscheme{alternate}{%
  \begin{tabular}[t]{@{}l@{ }l@{}}
  Name:  & \getcontact{name}{#1} \\
  Work:  & \getcontact{work}{#1} \\
  Email: & \texttt{\getcontact{email}{#1}}
  \end{tabular}%
}

\newcontact{foo}{
  name=Faa Foo,
  work=Univ.\ Blah,
  email=goo@goo.com
}
\newcontact{xxx}{
  name=XXX,
  work=YYY,
  email=ZZZ@goo.com
}

\begin{document}

\listcontact{foo}

\medskip

\listcontact{xxx}

\medskip

Short: \listcontact[short]{xxx}

\medskip

Alternate: \listcontact[alternate]{foo}

\medskip

\listallcontacts

\medskip

\listallcontacts[short]

\end{document}
egreg
  • 1,121,712
3

This is similar to Steven Seglets solution with slightly different packaging. You can show a single contact with \ShowContact{} or list all the contact with \listcontacts

enter image description here

Notes:

  • The values are stored based on #1 which is used as the key to access the contacts. An attempt to reuse the same key is now flagged as an error.
  • This has now been updated to add a \medskip between entries when the entire list is printed. The \medskip is not added before the first entry, nor after the last as shown in the figure.

Code:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter

\newtoggle{@IsFirstEntryInList}

\newcommand{@ShowContact}[1]{% \iftoggle{@IsFirstEntryInList}{% \global\togglefalse{@IsFirstEntryInList}% }{% \medskip% <-- separator between entries }% \ShowContact{#1}% } \newcommand{\ShowContact}[1]{% \par\noindent\textbf{\csuse{name #1}}% \par\noindent\textit{\csuse{work #1}}% \par\noindent\texttt{\csuse{email #1}}% }%

%% https://tex.stackexchange.com/a/14394/4301 \newcommand*{\listcontacts}{% Initialize \global\toggletrue{@IsFirstEntryInList}% } \newcommand{\AddToListOfContacts}[1]{% \g@addto@macro\listcontacts{{#1}}% }

\newcommand{\information}[3]{% \ifcsdef{name #1}{% \PackageError{\jobname}{Multiple uses of the key: '#1'}{}% }{% \csdef{name #1}{#1}% \csdef{work #1}{#2}% \csdef{email #1}{#3}% \AddToListOfContacts{\unexpanded{@ShowContact{#1}}}% }% } \makeatother

\information{Faa Foo}{Univ.\ Blah}{goo@goo.com} \information{Harvard}{Harvard University}{me@harvard.com} %\information{Harvard}{xxx University}{me@harvard.com}% <-- Triggers an error

\begin{document} \noindent To show one contact:

\ShowContact{Harvard}

\medskip\noindent To show all contacts:

\listcontacts \par\noindent Text following to check that there is no extra space at end... \end{document}

Peter Grill
  • 223,288
  • Interesting. In this case, the contacts are accessed via argument value, not via a number, right? – Sigur Nov 15 '18 at 18:28
  • @Sigur: Yes, the first parameter is used as the key to access the information. This is really helpful if you have a large number of contacts. – Peter Grill Nov 15 '18 at 18:30
  • Since I'm going to share this file, do you know if etoolbox comes with a basic installation? Because I know that tikz does not. – Sigur Nov 15 '18 at 18:30
  • Yes, etoolbox is in the standard distribution. But, so is tikz so not sure why you think otherwise. Also, have added error checking in the updated version. – Peter Grill Nov 15 '18 at 18:34
  • Oh, maybe I am wrong about tikz... it would be good. Since I don't use MikTeX... – Sigur Nov 15 '18 at 18:41
  • Don't know about MikTeX. But with TeX:Live those are standard. – Peter Grill Nov 15 '18 at 18:45
  • Do you know how to modify the format if the contact printed is the last one? I mean, I want to remove the vertical skip when inserting the last contact. – Sigur Nov 15 '18 at 22:10
  • @Sigur: Yeah I can do that. Easiest solution would be to move the \medskip to be before you print the first one. Is that an option? – Peter Grill Nov 15 '18 at 22:13
  • Hum, I can not have any space before it. Or do you think that we can first remove it and then move it, so before the 1st one no additional space is inserted. – Sigur Nov 15 '18 at 22:20
  • But in my case, it is more complicated since I am going to insert a symbol between, so the symbol can not be before the 1st neither after the last. – Sigur Nov 15 '18 at 22:21
  • 1
    @Sigur: As requested, I have updated it inserts a \medskip only between entries. You can replace the \medskip with the symbol you desire. – Peter Grill Nov 16 '18 at 06:39
  • This is perfect. Is \newtoggle part of etoolbox? It could be very useful to me in the future. Thanks for letting me know. – Sigur Nov 16 '18 at 14:14
  • Yes, \newtoggle is part of etoolbox. But there are many ways to do conditionals. See, for instance, Conditional typesetting / build. – Peter Grill Nov 16 '18 at 18:15
0

This "answer" does not answer your question.

A few remarks about the pitfalls of database-management in LaTeX

A few remarks about database-management in general:

Usually data is organized in so-called data-bases.

A data-base could, e.g., be a list of contact-details.

Each person or organization that you wish to contact might within the data-base be connected to a data-record which holds all the contact-details related to that person/organization.

Therefore a data-record itself could be organized by means of data-fields whereby each field serves for storing/providing information that belongs to a specific category.

Thus the logical structure of a data-base could be something like:

Data-base: contact-details
==========================

Data-record 001:
----------------
Value of data-field "Name":=John Doe
Value of data-field "Work":=whistle blower
Value of data-field "E-Mail":=Whistle@fake.email.address

Data-record 002:
----------------
Value of data-field "Name":=Jane Smith
Value of data-field "Work":=employée at department of alternative facts
Value of data-field "E-Mail":=AltFacts@fake.email.address

...

You need means

  • both for identifying each record that belongs to a data-base
  • and for keeping the single records that belong to a data-base distinguishable from each other.

The data-fields "Name" or "Work" or "E-Mail" are not suitable for this purpose.

E.g., some day there might be records about two different people in the data-base that have the same name.

Thus in data-base-management, it is common practise to indroduce in the structure of data-records a data-field which for each data-record holds a piece of information which does not occur within the corresponding data-field of any other data-record.

Usually this data-field is called a "primary key".

Often this primary-key is just some item of continuous numeration which is obtained by continuously numerating data-records.

When introducing a primary-key, the data-base "contact details" could look like this:

Data-base: contact-details
==========================

Data-record 001:
----------------
Value of data-field "Primary Key":=001
Value of data-field "Name":=John Doe
Value of data-field "Work":=whistle blower
Value of data-field "E-Mail":=Whistle@fake.email.address

Data-record 002:
----------------
Value of data-field "Primary Key":=002
Value of data-field "Name":=Jane Smith
Value of data-field "Work":=employée at department of alternative facts
Value of data-field "E-Mail":=AltFacts@fake.email.address

When doing this, you can - besides the single data-records - also maintain

  • a list of primary-keys belonging to the data-base an
  • a counter denoting the amount of records belonging to the data-base:

 

Data-base: contact-details
==========================

Amount of data-records: 2
List of primary-keys: {001}, {002}

Data-record 001:
----------------
Value of data-field "Primary Key":=001
Value of data-field "Name":=John Doe
Value of data-field "Work":=whistle blower
Value of data-field "E-Mail":=Whistle@fake.email.address

Data-record 002:
----------------
Value of data-field "Primary Key":=002
Value of data-field "Name":=Jane Smith
Value of data-field "Work":=employée at department of alternative facts
Value of data-field "E-Mail":=AltFacts@fake.email.address

The list of primary-keys would be of special interest for sorting out specific data-records:

Assume you wish to retrieve the e-mail-address of Jane Smith:

In terms of pseudo-code, you would do this via a directive like:

Iterate on that list of primary-keys that belongs to
the data-base "contact-details" as follows:

In each iteration "look" at the value of the data-field "Name"
of that data-record whose data-field "Primary Key" equals the 
current list element.
If that value equals "Jane Smith", print out the value of the
data-field "E-Mail" of that data-record.

Pitfalls related to (La)TeX:

When implementing such things in terms of (La)TeX, you will encounter a few problems which have to do with the fact that (La)TeX is a macro-language which does some pre-processing of input in terms of reading and tokenizing input:

For example, curly braces ({ and }) have a specific meaning in LaTeX and usually each opening-brace must habe a matching closing-brace and vice versa.

For example, the percent-character (%) has a specific meaning in LaTeX.

But according to RFC 2822 e-mail-adresses may contain unbalanced curly braces and percent-characters.

Therefore maintaining such entries via a data-base that is provided in terms of (La)TeX-input might be related to circumstances which the not-so experienced user might see as problems.

E.g., Alt}Fa%c#1ts@fake.email.address would be a nice e-mail-address.

But how to get the value Alt}Fa%c#1ts@fake.email.address into a data-record's data-field "E-Mail" in case the data-base in question is maintained in (La)TeX?

At first glimpse you could do it by means of macros that deliver the desired symbols in question:

Something like:

Value of data-field "E-Mail":=Alt\leftbrace Fa\textpercent c\hash1ts@fake.email.address

Also the names of people/organizations might contain characters/symbols which the input-encoding in which the (La)TeX-file is written does not contain.

Assume one of the persons whose contact-details are to be maintained has the name Alaïa Maëlys Gambolpütty while the data-base is written in a (La)TeX-input-file that is encoded in ASCII.
In such a case you must do it in terms of control-sequence-tokens whose names are formed by characters within the range of the input-encoding.

But such control-sequences make sorting out specific data-records difficult.

Therefore it might be a good idea to maintain each piece of data twice:

Once written only by means of characters of the input-encoding in question.
Once written in a way where also LaTeX-control-sequences that deliver the desired characters/symbols may occur:

Data-base: contact-details
==========================

Amount of data-records: 3
List of primary-keys: {001}, {002}, {003}

Data-record 001:
----------------
Value of data-field "Primary Key":=001
Value of data-field "Name" in input-encoding-representation:=John Doe
Value of data-field "Name" in LaTeX-representation:=John Doe
Value of data-field "Work" in input-encoding-representation:=whistle blower
Value of data-field "Work" in LaTeX-representation:=whistle blower
Value of data-field "E-Mail" in input-encoding-representation:=Whistle@fake.email.address
Value of data-field "E-Mail" in LaTeX-representation:=Whistle@fake.email.address

Data-record 002:
----------------
Value of data-field "Primary Key":=002
Value of data-field "Name" in input-encoding-representation:=Jane Smith
Value of data-field "Name" in LaTeX-representation:=Jane Smith
Value of data-field "Work" in input-encoding-representation:=employee at department of alternative facts
Value of data-field "Work" in LaTeX-representation:=employ\'ee at department of alternative facts
Value of data-field "E-Mail" in input-encoding-representation:=AltFacts@fake.email.address
Value of data-field "E-Mail" in LaTeX-representation:=AltFacts@fake.email.address

Data-record 003:
----------------
Value of data-field "Primary Key":=003
Value of data-field "Name" in input-encoding-representation:=Alaia Maelys Gambolpuetty
Value of data-field "Name" in LaTeX-representation:=Ala\"{\i}a Ma\"{e}lys Gambolp\"{u}tty
Value of data-field "Work" in input-encoding-representation:=employee at department of alternative facts
Value of data-field "Work" in LaTeX-representation:=employée at department of alternative facts
Value of data-field "E-Mail" in input-encoding-representation:=Alt}Fa%c#1ts@fake.email.address
Value of data-field "E-Mail" in LaTeX-representation:=Alt\leftbrace Fa\textpercent c\hash1ts@fake.email.address

...

With such an approach you could implement an interface where searching and sorting out takes place in terms of input-encoding-representation and delivers data in terms of LaTeX-representation.

With such an interface, one stage would always include reading and tokenizing the input-encoding-representations under verbatim-catcode-régime.

This is feasible but makes using the macros that form the interface

  • within pure-expansion-contexts impossible.
  • within the definition-texts of other macros cumbersome.

With such an interface, it would also be a good idea to have everything that denotes names of databases or names of data-fields or values in input-encoding-representation evaluate to characters of the input-encoding.

Therefore for proper error-checking a routine would be useful which does fully evaluate/expand arguments and then does check whether the expansion-result does hold character-tokens obeying the verbatim-catcode-régime only.

Full evaluation/expansion of an argument that might hold arbitrary tokens is already a nice problem.

An easy approach would be using \edef.
For one thing this also would break expandability of the interface.
For another thing \edef is not totally safe with arbitrary tokens.
E.g., look at

\edef\test{%
  Where does the definition text 
  stop?\iffalse{\fi Here?} Or Here?{\iffalse}\fi
}

I think a minimalist interface for some sort of database-management is feasible in LaTeX – with things like:

\NewInitializedDataRecord{<Primary Key>}%
                         {<Name>}%
                         {<Work>}%
                         {<EMail>}%


\NewEmptyDataRecord{<Primary Key>}
\AddNewDataFieldToDataRecord{<Primary Key>}{<Name of data-field>}{<Value>}%
\VerbatimizedAddNewDataFieldToDataRecord{<Primary Key>}{<Name of data-field>}|<Value>|% <- verbatim-arg
(extraction of verbatimized values of data-fields via \scantokens...)

\PrintFieldValueFromDataRecord{<Primary Key>}{<Name of data-field>}

\PrintDataRecord{<Primary Key>}

\PrintAllDataRecords

\PrintAllDataRecordsWithSpecificDatFieldValue{<Name of data-field>}{<Specific Value>}

But before going into details, specification about the tokens that might form values of data-fields and about their treatment when comparing values of data-fields is needed.

Ulrich Diez
  • 28,770
  • I am not sure if I understood your comments. Anyway, thanks for attention. Let me inform that the contacts come from authors of a paper, so I think that two exactly equal names could happen rarely. – Sigur Nov 16 '18 at 18:13
  • Your question actually is about some sort of whatsoever minimalist database-management. :-) I extended my answer in the hope of pointing out some of the pitfalls related to whatsoever minimalist database-management in terms of a macro language. :-) – Ulrich Diez Nov 16 '18 at 21:29
  • Well, thanks for your work. But I confess that I have no idea how to understand it... lol – Sigur Nov 16 '18 at 21:33
  • I think you want to say “From relational algebra we know that it is not a good idea to assign to each data-record its own unique primary-key as a data-field of its own of that data-record.”. – egreg Nov 16 '18 at 21:48
  • @UlrichDiez The formulation is way better now. For the same reason I assigned a label to each contact. – egreg Nov 16 '18 at 22:31