关联数组

关联数组

我试图理解 在命令中存储字符串数组 但找不到以下问题的(明显的)解决方案:

我有关于作曲家、标题、副标题和其他一些由数字字符串 id 索引的元数据的信息,例如 00034、00035、02354、12335(它们都是以零开头的 5 位数字),我想将其存储在关联数组(或者可能是矩阵?)中,并能够在文档的各个位置调用该信息,例如

\data{00034}{composer}

以及各种“文本操作”,例如

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

使用 LaTeX 可以实现吗?TIA!

编辑 1:我忘了提到任何“定义”或“填充”命令块都是由数据库导出生成的,因此如下面的许多优秀解决方案中所示的大量“设置”命令块不是问题。

编辑 2:@Marijn 建议看另一篇文章,但尽管它可能对某些 TeX-Wizard 有用,但对我来说却没用,因为水平比较低。这里的回复很棒,很有帮助。

答案1

在此处输入图片描述

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

答案2

这显然是财产清单的工作。

查看\uselists命令以了解原因:我们可以映射已知列表;中间参数是一个模板,#1代表当前 ID。

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

在此处输入图片描述

答案3

似乎您有一个记录数组,因此每个数组元素本身都是一个具有数据字段的记录。

您可以为每个数据项定义一个控制序列:

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

您可以使用 expl3 的包 l3prop 并维护一个属性列表,其中属性名称由数组元素名称和数组元素字段名称组合而成:

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

您可以尝试嵌套\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}

如果数据以 .csv 列表形式提供,则包数据工具可能感兴趣:

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

相关内容