根据 CSV 文件中的单列创建已排序的三列表格

根据 CSV 文件中的单列创建已排序的三列表格

我有一个包含多个列的 CSV 文件。其中一列称为 summits。此列包含多个 summits,格式如下:

Summit name (elevation m)

例如

Mount Everest (8848m)

我想要做的是创建一个表格,按降序列出所有独特的峰会。例如像这样:

在此处输入图片描述

我已经能够使用 datatool 包创建一个包含唯一条目的列表。

\DTLforeach*{tours}{\Summit=summits}{%
  \expandafter\DTLifinlist\expandafter{\Summit}{\uniquesummit}%
  {}
  {%
    \ifdefempty{\uniquesummit}%
    {\let\uniquesummit\Summit}% 
    {% 
      \eappto\uniquesummit{,\Summit}%
    }%
  }%
}

剩下的是:

  • 区分山顶名称和海拔
  • 排序
  • 创建表

我以为我已经知道如何创建表格了这个答案,但在我的例子中,所有峰会都在第一行,而不是分散在多行中。我猜原因是我的列表形式略有不同?但为什么呢?

以下是一个可以更好地描述我的问题的 MWE。请注意,在实际的 CSV 文件中,; 是一个制表符。我无法让它在 MWE 中使用制表符:

\documentclass{article}
\usepackage{filecontents}
\usepackage{datatool}

\begin{filecontents*}{summits.csv}
title;summits;description
test1;Matterhorn (4500m);description1
test2;Mount Everest (8848m);description2
test3;K2 (8611m), Kangchenjunga (8586m);description3
test4;Mount Everest (8848m);description4
\end{filecontents*}

\begin{document}
    \DTLsetseparator{;}%
    \DTLloaddb{tours}{summits.csv}
    
    \newcommand*{\uniquesummit}{}

    \DTLforeach*{tours}{\Summit=summits}{%
        \expandafter\DTLifinlist\expandafter{\Summit}{\uniquesummit}%
        {}{
            \ifdefempty{\uniquesummit}%
            {\let\uniquesummit\Summit}%
        {%
            \eappto\uniquesummit{,\Summit}%
            }%
        }%
    }

% Shows that I can find the unique summits
List of unique summits: \uniquesummit.

\bigskip


% This I would like to generate based on the summits.csv
\begin{tabular}{l l l}
1 &Mount Everest & 8848\\
2 & K2 & 8611\\
3 & Kangchenjunga & 8586\\
4 & Matterhorn & 4500\\
\end{tabular}
    
    
\end{document}

答案1

以下用于expl3数据处理。其工作方式如下:

首先读取文件的第一行并解析标题。找到名为的列summits,以便在进一步处理时我们知道哪一列是感兴趣的。

接下来逐行读取文件并提取第 $n$ 列(名为 的列summits)。用逗号分隔该列(以获取该行中的所有峰),然后通过正则表达式提取每个峰的名称和高度。将找到的值放入单个seq变量中。

之后,我们从中删除重复项seq,并按山的高度进行排序(降序)。

然后输出表格。seq现在已排序的表格中的每个元素都被赋予了\readmountains_output_single:n每行的格式。

这一切都由宏完成\readmountains,它接受一个可选参数和一个强制参数。强制参数是文件名,可选参数是列分隔符(默认为 Tab,如果^^I你想直接从 TeX 中输入,但可选参数希望分隔符转义,因此你需要提供\^^I一个 Tab,这是默认值)。

(正在进行一些错误检查,因此

\documentclass[]{article}

\begin{filecontents*}{\jobname.csv}
title;summits;description
test1;Matterhorn (4500m);description1
test2;Mount Everest (8848m);description2
test3;K2 (8611m), Kangchenjunga (8586m);description3
test4;Mount Everest (8848m);description4
\end{filecontents*}

\ExplSyntaxOn
\ior_new:N \g_readmountains_file_ior
\seq_new:N \l_readmountains_mountains_seq
\seq_new:N \l_readmountains_extract_seq
\int_new:N \g_readmountains_output_int
\int_new:N \l_readmountains_col_int
\int_new:N \l_readmountains_catcode_int
\int_new:N \l_readmountains_line_int
\seq_new:N \l_readmountains_csv_seq
\scan_new:N \s_readmountains_end
\msg_new:nnn { readmountains } { no-summits }
  { Column ~ `summits' ~ not ~ found. ~ Aborting. }
\msg_new:nnn { readmountains } { short-line }
  {
    Short ~ line ~ encountered ~ in ~ line ~
    \int_use:N \l_readmountains_line_int. ~ Aborting.
  }
\msg_new:nnn { readmountains } { malformatted-summit }
  {
    Malformatted ~ summit ~ encountered ~ in ~ line ~
    \int_use:N \l_readmountains_line_int. ~ Aborting.
  }
\NewDocumentCommand \readmountains { O{\^^I} m }
  {
    \readmountains:Nn #1 {#2}
  }
\cs_new_protected:Npn \readmountains:Nn #1#2
  {
    \readmountains_read_file:NnN #1 {#2} \l_readmountains_mountains_seq
    \seq_remove_duplicates:N \l_readmountains_mountains_seq
    \readmountains_sort:N \l_readmountains_mountains_seq
    \readmountains_output:N \l_readmountains_mountains_seq
    \use_none:n \s_readmountains_end
  }
\cs_new_protected:Npn \readmountains_read_file:NnN #1#2#3
  {
    \int_set:Nn \l_readmountains_catcode_int { \char_value_catcode:n { `#1 } }
    \char_set_catcode_other:N #1
    \ior_open:Nn \g_readmountains_file_ior {#2}
    \seq_clear:N #3
    \ior_get:NN \g_readmountains_file_ior \l_tmpa_tl
    \exp_args:NNx \seq_set_split:NnV \l_readmountains_csv_seq
      { \char_generate:nn { `#1 } { 12 } }
      \l_tmpa_tl
    \int_zero:N \l_readmountains_col_int
    \seq_map_inline:Nn \l_readmountains_csv_seq
      {
        \int_incr:N \l_readmountains_col_int
        \str_if_eq:nnT { summits } {##1} { \seq_map_break: }
      }
    \str_if_eq:eeF
      { \seq_item:Nn \l_readmountains_csv_seq \l_readmountains_col_int }
      { summits }
      {
        \msg_error:nn { readmountains } { no-summits }
        \readmountains_use_none_delimit_by_s_end:w
      }
    \int_set:Nn \l_readmountains_line_int \c_one_int
    \ior_map_inline:Nn \g_readmountains_file_ior
      {
        \int_incr:N \l_readmountains_line_int
        \exp_args:NNx \seq_set_split:Nnn \l_readmountains_csv_seq
          { \char_generate:nn { `#1 } { 12 } }
          {##1}
        \int_compare:nNnT
          \l_readmountains_col_int > { \seq_count:N \l_readmountains_csv_seq }
          {
            \msg_error:nn { readmountains } { short-line }
            \ior_map_break:n { \readmountains_use_none_delimit_by_s_end:w }
          }
        \exp_args:Nnnx
        \seq_set_split:Nnn \l_readmountains_csv_seq {,}
          { \seq_item:Nn \l_readmountains_csv_seq \l_readmountains_col_int }
        \seq_map_inline:Nn \l_readmountains_csv_seq
          {
            \tl_if_empty:nF { ####1 }
              {
                \regex_extract_once:nnNF { (.*)\s\((\d+)m\) } { ####1 }
                  \l_readmountains_extract_seq
                  {
                    \msg_error:nn { readmountains } { malformatted-summit }
                    \readmountains_use_none_delimit_by_s_end:w
                  }
                \seq_push:Nx #3
                  {
                    { \seq_item:Nn \l_readmountains_extract_seq {2} }
                    { \seq_item:Nn \l_readmountains_extract_seq {3} }
                  }
                \use_none:n \s_readmountains_end
              }
          }
      }
    \ior_close:N \g_readmountains_file_ior
    \char_set_catcode:nn { `#1 } \l_readmountains_catcode_int
  }
\cs_new_protected:Npn \readmountains_sort:N #1
  {
    \seq_sort:Nn #1
      {
        \int_compare:nNnTF { \use_ii:nn ##1 } < { \use_ii:nn ##2 }
          \sort_return_swapped:
          \sort_return_same:
      }
  }
\cs_new_protected:Npn \readmountains_output:N #1
  {
    \int_gzero:N \g_readmountains_output_int
    \begin { tabular } { r l r }
      \seq_map_function:NN #1 \readmountains_output_single:n
    \end { tabular }
  }
\cs_new_protected:Npn \readmountains_output_single:n #1
  {
    \int_gincr:N \g_readmountains_output_int
    \int_use:N \g_readmountains_output_int .
    & \use_i:nn #1 
    & \use_ii:nn #1
    \\
  }
\cs_new:Npn \readmountains_use_none_delimit_by_s_end:w #1 \s_readmountains_end
  {}
\ExplSyntaxOff

\begin{document}
% since the file was written with ; as the delimiter use that
\readmountains[\;]{\jobname.csv}
\end{document}

在此处输入图片描述

相关内容