Matrices asociativas

Matrices asociativas

Estaba tratando de entender el artículo dado en Almacenar una serie de cadenas en un comando pero no pude encontrar la solución (obvia) al siguiente problema:

Tengo información sobre el compositor, título, subtítulo y algunos otros metadatos indexados por una identificación de cadena numérica, por ejemplo, 00034, 00035, 02354, 12335 (todos son de 5 dígitos con ceros a la izquierda) y quiero almacenar esto en una matriz asociativa ( ¿O tal vez matriz?) y ser capaz de recordar esa información en varios lugares de un documento, por ejemplo

\data{00034}{composer}

y también en varias 'manipulaciones de texto', por ejemplo

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

¿Es esto posible usando LaTeX? TÍA!

Edición 1: Olvidé mencionar que cualquier bloque de comandos de "definiciones" o "población" se genera mediante una exportación de base de datos, por lo que los bloques grandes de comandos "establecidos", como se muestran en las excelentes soluciones a continuación, no son un problema.

Edición 2: @Marijn sugirió otro artículo, pero si bien puede ser útil para algunos TeX-Wizard, no lo fue para mí, eso es más bien en un nivel bajo. Las respuestas aquí son excelentes y ayudan mucho.

Respuesta1

ingrese la descripción de la imagen aquí

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

Respuesta2

Obviamente, este es un trabajo para listas de propiedades.

Vea el \uselistscomando para ver por qué: podemos mapear las listas conocidas; el argumento del medio es una plantilla que #1representa la identificación actual.

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

ingrese la descripción de la imagen aquí

Respuesta3

Parece que tiene una serie de registros, de modo que cada elemento de la matriz en sí es un registro que tiene campos de datos.

Puede definir una secuencia de control para cada elemento de datos:

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

Puede usar el paquete l3prop de expl3 y mantener una lista de propiedades donde los nombres de las propiedades se combinan a partir de los nombres de los elementos de la matriz y los nombres de los campos de los elementos de la matriz:

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

Puedes intentar anidar \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}

En caso de que los datos estén disponibles como .csv-list, el paqueteherramienta de datospodría ser de interés:

% 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}

información relacionada