Assoziative Arrays

Assoziative Arrays

Ich habe versucht, den Artikel zu verstehen, der unter Speichern eines Arrays von Zeichenfolgen in einem Befehl konnte aber die (offensichtliche) Lösung für das folgende Problem nicht finden:

Ich habe Informationen über Komponist, Titel, Untertitel und einige andere Metadaten, die durch eine numerische Zeichenfolge-ID indiziert sind, z. B. 00034, 00035, 02354, 12335 (sie sind alle 5-stellig mit führenden Nullen) und ich möchte diese in einem assoziativen Array (oder vielleicht einer Matrix?) speichern und diese Informationen an verschiedenen Stellen in einem Dokument abrufen können, z. B.

\data{00034}{composer}

und auch in verschiedenen 'Textmanipulationen' zB

\data{00034}{composer}: \data{00034}{title}, \emph{\data{00034}{subtitle}}

Ist dies mit LaTeX möglich? Danke!

Bearbeitung 1: Ich habe vergessen zu erwähnen, dass alle Blöcke mit „Definitionen“- oder „Population“-Befehlen durch einen Datenbankexport generiert werden, sodass große Blöcke mit „Set“-Befehlen, wie in den vielen guten Lösungen unten gezeigt, kein Problem darstellen.

Edit 2: @Marijn hat einen anderen Artikel vorgeschlagen, aber obwohl dieser für manche TeX-Experten nützlich sein könnte, war er das nicht für mich, das ist eher auf niedrigem Niveau. Die Antworten hier sind ausgezeichnet und helfen sehr.

Antwort1

Bildbeschreibung hier eingeben

\documentclass{article}

\newcommand\savedata[3]{\expandafter\def\csname data-#1-#2\endcsname{#3}}
\newcommand\data[2]{\csname data-#1-#2\endcsname}

\savedata{00034}{composer}{Beethoven}
\savedata{00034}{title}{Bagatelle No. 25}
\savedata{00034}{subtitle}{Für Elise}

\begin{document}

\data{00034}{composer}: \data{00034}{title}, \emph{\data{00034}{subtitle}}

\end{document}

Antwort2

Dies ist offensichtlich eine Aufgabe für Eigenschaftslisten.

Sehen Sie sich den \uselistsBefehl an, um zu erfahren, warum: Wir können die bekannten Listen zuordnen; das mittlere Argument ist eine Vorlage, wobei #1für die aktuelle ID steht.

\documentclass{article}
\usepackage{booktabs}

\ExplSyntaxOn

\NewDocumentCommand{\definelist}{mm}
 {
  \mflxvii_list_define:nn { #1 } { #2 }
 }
\NewDocumentCommand{\addtolist}{mmm}
 {
  \mflxvii_list_add:nnn { #1 } { #2 } { #3 }
 }
\NewExpandableDocumentCommand{\getitem}{mm}
 {
  \mflxvii_list_get:nn { #1 } { #2 }
 }
\NewDocumentCommand{\uselists}{mmm}
 {% #1 = pre code, #2 = body, #3 = post code
  \mflxvii_list_use:nnn { #1 } { #2 } { #3 }
 }

\seq_new:N \g_mflxvii_list_all_seq

% check whether the list already exists
\cs_new_protected:Nn \mflxvii_list_define:nn
 {
  \prop_if_exist:cTF { g_mflxvii_list_#1_prop }
   {% already defined
    \msg_error:nnn { mflxvii } { list-exist } { #1 }
   }
   {% define a new one
    % add to the global list of lists
    \seq_gput_right:Nn \g_mflxvii_list_all_seq { #1 }
    % build the list from the given data
    \prop_new:c { g_mflxvii_list_#1_prop }
    \prop_gset_from_keyval:cn { g_mflxvii_list_#1_prop } { #2 }
   }
 }

\cs_new_protected:Nn \mflxvii_list_add:nnn
 {
  \prop_if_exist:cTF { g_mflxvii_list_#1_prop }
   {% add if existing
    \prop_gput:cnn { g_mflxvii_list_#1_prop } { #2 } { #3 }
   }
   {% list doesn't exist
    \msg_error:nnn { mflxvii } { list-exist } { #1 }
   }
 }

\cs_new:Nn \mflxvii_list_get:nn
 {
  \prop_item:cn { g_mflxvii_list_#1_prop } { #2 }
 }

\cs_new_protected:Nn \mflxvii_list_use:nnn
 {
  \group_begin:
  \cs_set:Nn \__mflxvii_list_use_body:n { #2 }
  % pre code
  #1
  % body
  \seq_map_function:NN \g_mflxvii_list_all_seq \__mflxvii_list_use_body:n
  % post code
  #3
  \group_end:
 }

\ExplSyntaxOff

\definelist{00034}{
  composer=Beethoven,
  title=Bagatelle No.\@ 25,
  subtitle=Für Elise
}
\definelist{00035}{
  composer=Bob Geldof,
  title=The Great Song of Indifference
}
\addtolist{00035}{subtitle}{I don't mind}

\begin{document}

\getitem{00034}{composer}: \getitem{00034}{title}, \getitem{00034}{subtitle}

\bigskip

\uselists{%
  \begin{tabular}{@{}llll@{}}
  \toprule
  ID & composer & title & subtitle \\
  \midrule
}{ #1 & \getitem{#1}{composer} & \getitem{#1}{title} & \getitem{#1}{subtitle} \\
}{%
  \bottomrule\end{tabular}
}

\end{document}

Bildbeschreibung hier eingeben

Antwort3

Sieht so aus, als hätten Sie ein Array von Datensätzen, sodass jedes Array-Element selbst ein Datensatz ist, der Datenfelder hat.

Sie können für jedes Datenelement eine Steuersequenz definieren:

\documentclass{article}

\ExplSyntaxOn
\cs_new:Npn \StoreData #1#2#3 {
  \cs_new:cpn {ArrayItem:{#1}_RecordField:{#2}} {#3}
}
\cs_new:Npn \Data #1#2 {
  \cs_if_exist_use:cF {ArrayItem:{#1}_RecordField:{#2}} {--data~not~available--}
}
\ExplSyntaxOff

%-------------------------------------------------------------------
\StoreData {00034}{composer}{Beethoven}
\StoreData {00034}{title}{Bagatelle No.\@ 25}
\StoreData {00034}{subtitle}{Für Elise}
%-------------------------------------------------------------------
\StoreData {00035}{composer}{Bob Geldof}
\StoreData {00035}{title}{The Great Song of Indifference}
\StoreData {00035}{subtitle}{I don't mind}
%-------------------------------------------------------------------

\begin{document}

\Data{00034}{composer}\par
\Data{00034}{title}\par
\Data{00034}{subtitle}\par
\Data{00035}{composer}\par
\Data{00035}{title}\par
\Data{00035}{subtitle}\par
\Data{00035}{Unknown}\par
\Data{00036}{subtitle}

\end{document}

Sie können das Paket l3prop von expl3 verwenden und eine Eigenschaftenliste verwalten, in der die Eigenschaftennamen aus den Namen der Array-Elemente und den Namen der Felder der Array-Elemente kombiniert werden:

\documentclass{article}

\ExplSyntaxOn
\prop_new:N \g__MyModule_Array_prop
\cs_new:Npn \AddArrayProperty #1#2#3 {
  \prop_gput:Nnn \g__MyModule_Array_prop {ArrayItem:{#1}_RecordField:{#2}} {#3}
}
\cs_new:Npn \Data #1#2 {
  \prop_if_in:NnTF
    \g__MyModule_Array_prop
    {ArrayItem:{#1}_RecordField:{#2}} 
    { \prop_item:Nn \g__MyModule_Array_prop {ArrayItem:{#1}_RecordField:{#2}} }
    {--data~not~available--}
}
\ExplSyntaxOff

%-------------------------------------------------------------------
\AddArrayProperty {00034}{composer}{Beethoven}
\AddArrayProperty {00034}{title}{Bagatelle No.\@ 25}
\AddArrayProperty {00034}{subtitle}{Für Elise}
%-------------------------------------------------------------------
\AddArrayProperty {00035}{composer}{Bob Geldof}
\AddArrayProperty {00035}{title}{The Great Song of Indifference}
\AddArrayProperty {00035}{subtitle}{I don't mind}
%-------------------------------------------------------------------

\begin{document}

\Data{00034}{composer}\par
\Data{00034}{title}\par
\Data{00034}{subtitle}\par
\Data{00035}{composer}\par
\Data{00035}{title}\par
\Data{00035}{subtitle}\par
\Data{00035}{Unknown}\par
\Data{00036}{subtitle}

\end{document}

Sie können es mit Verschachtelung versuchen \str_case:nnF:

\documentclass{article}

\newcommand\MyArrayOfRecords{%
  {00034}{%
    {composer}{Beethoven}%
    {title}{Bagatelle No.\@ 25}%
    {subtitle}{Für Elise}%
  }%
  {00035}{%
    {composer}{Bob Geldof}%
    {title}{The Great Song of Indifference}%
    {subtitle}{I don't mind}%
  }%
}%


\ExplSyntaxOn
\cs_new:Nn \__MyStuff_Check_Item:nnn {
  \quark_if_no_value:nTF 
    {#1}
    {--#2~not~available--}
    { \str_case:nnF {#3}{#1}{--#3~not~available~in~#2--} }
}
\cs_new:Npn \Data #1#2 {
  \exp_args:Nf
    \__MyStuff_Check_Item:nnn 
      { \exp_args:Nno  \str_case:nnF {#1} { \MyArrayOfRecords } {\exp_not:N \q_no_value} }  
      {#1} 
      {#2}
}
\ExplSyntaxOff

\begin{document}

\Data{00034}{composer}\par
\Data{00034}{title}\par
\Data{00034}{subtitle}\par
\Data{00035}{composer}\par
\Data{00035}{title}\par
\Data{00035}{subtitle}\par
\Data{00035}{Unknown}\par
\Data{00036}{subtitle}

\end{document}

Liegen die Daten als .csv-Liste vor, wird das PaketDatentoolkönnte interessant sein:

% Have LaTeX create some dummy .csv-files. 
% In a real life scenario the .csv-files could come from the software you use
% for maintaining and exporting your data.

\begin{filecontents*}{MyMusic1.csv}
num,comp,ttl,sbttl
00034,Beethoven,Bagatelle No.\@ 25,Für Elise
00035,Bob Geldof,The Great Song of Indifference,I don't mind
\end{filecontents*}

\begin{filecontents*}{MyMusic2.csv}
sbttl,ttl,comp,num
Der für die Sünde der Welt gemarterte und sterbende Jesus,Brockes-Passion,Georg Friedrich Händel,00047
\end{filecontents*}

\begin{filecontents*}{MyMusic3.csv}
Jean Sibelius,Finlandia,Opus 26,01769
Monty Python,,Worried,00036
\end{filecontents*}

\documentclass{article}
\usepackage{datatool}

% To create new database from a .csv-file with \DTLloaddb set the
% switch \DTLnewdbonloadtrue ; this is the default on the start:
% \DTLnewdbonloadtrue

\DTLloaddb[%
              % MyMusic1.csv has a header row, thus noheader=false:
            noheader=false, 
              %   Let's override the field-names given in the .csv-file's header row by new names 
              %   given in keys={...} in the order in which the old names occur in the header-row:
            keys={ID,composer,title,subtitle}
          ]{MusicDatabase}{MyMusic1.csv}

% To apennd  with \DTLloaddb to an existing database data stemming 
% from a .csv-file set the switch \DTLnewdbonloadfalse:
\DTLnewdbonloadfalse

\DTLloaddb[%
            noheader=false,
            keys={subtitle,title,composer,ID}
          ]{MusicDatabase}{MyMusic2.csv}

\DTLloaddb[%
            noheader=true,%
            keys={composer,subtitle,title,ID},
              % With the last call to \DTLloaddb set the column-headings in case of
              % using facilities of the package datatool for creating tables 
              %(tabular/longtable) from data of the database. You can change this
              % setting laterwards via \DTLsetheader.
            headers={%
              Numerical String ID,Composer,Title,Subtitle%
            }
          ]{MusicDatabase}{MyMusic3.csv}

%----------------------------------------------------------------------------------------
% \GetDataFieldEntryByID{<ID>}%
%                       {<datafield>}%
%                       {<tokens in case only ID does not exist>}%
%                       {<tokens in case only datafield does not exist>}%
%                       {<tokens in case both ID and datafield don't exist>}%
%                       {<tokens in case datafield does exist but does not hold data>}
\newcommand\MyScratchMacro{}%
\newcommand\GetDataFieldEntryByID[6]{%
  \begingroup
  \DTLifhaskey{MusicDatabase}{#2}{%
    \DTLgetvalueforkey{\MyScratchMacro}{ID}{MusicDatabase}{ID}{#1}%
    \DTLifnullorempty{\MyScratchMacro}{%
      \endgroup#3%
    }{%
      \DTLgetvalueforkey{\MyScratchMacro}{#2}{MusicDatabase}{ID}{#1}%
      \DTLifnullorempty{\MyScratchMacro}%
                       {\endgroup#6}%
                       {\expandafter\endgroup\MyScratchMacro}%
    }%
  }{%
    \DTLgetvalueforkey{\MyScratchMacro}{ID}{MusicDatabase}{ID}{#1}%
    \DTLifnullorempty{\MyScratchMacro}{%
      \endgroup#5%
    }{%
      \endgroup#4%
    }%
  }%
}%
%----------------------------------------------------------------------------------------

\begin{document}

% Sort the database. This affects the order in which rows of the database are processed when
% iterating the database from the first to the last row, e.g. in the course if crearing a table
% where all database rows are displayed.
\dtlsort{ID=ascending}{MusicDatabase}{\dtlcompare}%
%
% Have datatool create a table with all database-rows, don't display the subtitle-column:
\begingroup
\scriptsize
\DTLdisplaydb[subtitle]{MusicDatabase}%
\par
\endgroup

\newpage

\GetDataFieldEntryByID{00034}%
                      {composer}%
                      {Error: Database does not have a row with ID 00034}%
                      {Error: Database does not have a key/datafield/column ``composer''}%
                      {Error: Database neither does have row with ID 00034 nor does have a key/datafield/column ``composer''}%
                      {Error: The row with the ID 00034 does not have data in key/datafield/column ``composer''}

\GetDataFieldEntryByID{00034}%
                      {title}%
                      {Error: Database does not have a row with ID 00034}%
                      {Error: Database does not have a key/datafield/column ``title''}%
                      {Error: Database neither does have row with ID 00034 nor does have a key/datafield/column ``title''}%
                      {Error: The row with the ID 00034 does not have data in key/datafield/column ``title''}

\GetDataFieldEntryByID{00034}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 00034}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 00034 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 00034 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{00034}%
                      {ID}%
                      {Error: Database does not have a row with ID 00034}%
                      {Error: Database does not have a key/datafield/column ``ID''}%
                      {Error: Database neither does have row with ID 00034 nor does have a key/datafield/column ``ID''}%
                      {Error: The row with the ID 00034 does not have data in key/datafield/column ``ID''}


\medskip\hrule\medskip

\GetDataFieldEntryByID{00035}%
                      {composer}%
                      {Error: Database does not have a row with ID 00035}%
                      {Error: Database does not have a key/datafield/column ``composer''}%
                      {Error: Database neither does have row with ID 00035 nor does have a key/datafield/column ``composer''}%
                      {Error: The row with the ID 00035 does not have data in key/datafield/column ``composer''}

\GetDataFieldEntryByID{00035}%
                      {title}%
                      {Error: Database does not have a row with ID 00035}%
                      {Error: Database does not have a key/datafield/column ``title''}%
                      {Error: Database neither does have row with ID 00035 nor does have a key/datafield/column ``title''}%
                      {Error: The row with the ID 00035 does not have data in key/datafield/column ``title''}

\GetDataFieldEntryByID{00035}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 00035}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 00035 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 00035 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{00035}%
                      {ID}%
                      {Error: Database does not have a row with ID 00035}%
                      {Error: Database does not have a key/datafield/column ``ID''}%
                      {Error: Database neither does have row with ID 00035 nor does have a key/datafield/column ``ID''}%
                      {Error: The row with the ID 00035 does not have data in key/datafield/column ``ID''}


\medskip\hrule\medskip

\GetDataFieldEntryByID{00036}%
                      {composer}%
                      {Error: Database does not have a row with ID 00036}%
                      {Error: Database does not have a key/datafield/column ``composer''}%
                      {Error: Database neither does have row with ID 00036 nor does have a key/datafield/column ``composer''}%
                      {Error: The row with the ID 00036 does not have data in key/datafield/column ``composer''}

\GetDataFieldEntryByID{00036}%
                      {title}%
                      {Error: Database does not have a row with ID 00036}%
                      {Error: Database does not have a key/datafield/column ``title''}%
                      {Error: Database neither does have row with ID 00036 nor does have a key/datafield/column ``title''}%
                      {Error: The row with the ID 00036 does not have data in key/datafield/column ``title''}

\GetDataFieldEntryByID{00036}%
                      {ID}%
                      {Error: Database does not have a row with ID 00036}%
                      {Error: Database does not have a key/datafield/column ``ID''}%
                      {Error: Database neither does have row with ID 00036 nor does have a key/datafield/column ``ID''}%
                      {Error: The row with the ID 00036 does not have data in key/datafield/column ``ID''}


\medskip\hrule\medskip

\GetDataFieldEntryByID{00047}%
                      {composer}%
                      {Error: Database does not have a row with ID 00047}%
                      {Error: Database does not have a key/datafield/column ``composer''}%
                      {Error: Database neither does have row with ID 00047 nor does have a key/datafield/column ``composer''}%
                      {Error: The row with the ID 00047 does not have data in key/datafield/column ``composer''}

\GetDataFieldEntryByID{00047}%
                      {title}%
                      {Error: Database does not have a row with ID 00047}%
                      {Error: Database does not have a key/datafield/column ``title''}%
                      {Error: Database neither does have row with ID 00047 nor does have a key/datafield/column ``title''}%
                      {Error: The row with the ID 00047 does not have data in key/datafield/column ``title''}

\GetDataFieldEntryByID{00047}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 00047}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 00047 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 00047 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{00047}%
                      {ID}%
                      {Error: Database does not have a row with ID 00047}%
                      {Error: Database does not have a key/datafield/column ``ID''}%
                      {Error: Database neither does have row with ID 00047 nor does have a key/datafield/column ``ID''}%
                      {Error: The row with the ID 00047 does not have data in key/datafield/column ``ID''}


\medskip\hrule\medskip

\GetDataFieldEntryByID{01769}%
                      {composer}%
                      {Error: Database does not have a row with ID 01769}%
                      {Error: Database does not have a key/datafield/column ``composer''}%
                      {Error: Database neither does have row with ID 01769 nor does have a key/datafield/column ``composer''}%
                      {Error: The row with the ID 01769 does not have data in key/datafield/column ``composer''}

\GetDataFieldEntryByID{01769}%
                      {title}%
                      {Error: Database does not have a row with ID 01769}%
                      {Error: Database does not have a key/datafield/column ``title''}%
                      {Error: Database neither does have row with ID 01769 nor does have a key/datafield/column ``title''}%
                      {Error: The row with the ID 01769 does not have data in key/datafield/column ``title''}

\GetDataFieldEntryByID{01769}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 01769}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 01769 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 01769 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{01769}%
                      {ID}%
                      {Error: Database does not have a row with ID 01769}%
                      {Error: Database does not have a key/datafield/column ``ID''}%
                      {Error: Database neither does have row with ID 01769 nor does have a key/datafield/column ``ID''}%
                      {Error: The row with the ID 01769 does not have data in key/datafield/column ``ID''}

\medskip\hrule\medskip

Error-triggering:

\GetDataFieldEntryByID{00036}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 00036}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 00036 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 00036 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{00050}%
                      {subtitle}%
                      {Error: Database does not have a row with ID 00050}%
                      {Error: Database does not have a key/datafield/column ``subtitle''}%
                      {Error: Database neither does have row with ID 00050 nor does have a key/datafield/column ``subtitle''}%
                      {Error: The row with the ID 00050 does not have data in key/datafield/column ``subtitle''}

\GetDataFieldEntryByID{00036}%
                      {year}%
                      {Error: Database does not have a row with ID 00036}%
                      {Error: Database does not have a key/datafield/column ``year''}%
                      {Error: Database neither does have row with ID 00036 nor does have a key/datafield/column ``year''}%
                      {Error: The row with the ID 00036 does not have data in key/datafield/column ``year''}

\GetDataFieldEntryByID{00050}%
                      {year}%
                      {Error: Database does not have a row with ID 00050}%
                      {Error: Database does not have a key/datafield/column ``year''}%
                      {Error: Database neither does have row with ID 00050 nor does have a key/datafield/column ``year''}%
                      {Error: The row with the ID 00050 does not have data in key/datafield/column ``year''}

\end{document}

verwandte Informationen