2

The code below is taken from this post. Is it possible to create a sequence/array that contains all the different years, and another that contains how many entries per year there are?

\documentclass{tufte-book}
\usepackage{datenumber, ifthen, xparse}
\usepackage{lipsum}
\setstartyear{2000}
\newcounter{day}

\ExplSyntaxOn

% Declare variables
\seq_new:N \g_journal_seq
\seq_new:N \g_journal_out_seq
\iow_new:N \g_journal_stream
\tl_new:N \l_journal_date_tl

% At the beginning of the run, read the lines of the `.jrn` file into a sequence.
% These are the dates. If the file can not be opened, it probably does not exists
% and we treat it as empty.
\cs_new:Npn \readjournaldates {
  \ior_open:NnT \g_journal_stream { \c_sys_jobname_str.jrn } {
    \ior_map_inline:Nn \g_journal_stream {
      \seq_gput_right:Nn \g_journal_seq { ##1 }
    }
    \ior_close:N \g_journal_stream
  }
}

% The main environment:
\NewDocumentEnvironment{ newday }{ O{} }{
  \stepcounter{day}
  % If the sequence \g_journal_seq is not empty yet, then we already saved a date
  % for the current day. Save this day in `\l_journal_date_tl` and delete it from
  % the sequence. Otherwise we have not saved anything yet, so we choose the current
  % date.
  \seq_gpop_left:NNF \g_journal_seq \l_journal_date_tl {
    \tl_set:Nx \l_journal_date_tl {\datedayname,~\today}
  }
  % Now we have to save the choosen date for the next run. First, only store it in the
  % sequence `\g_journal_out_seq`, we only write it to the file at the end to avoid
  % overwriting the file if something fails:
  \seq_gput_right:NV \g_journal_out_seq \l_journal_date_tl
  \textbf{ #1 }
  \marginnote{ \l_journal_date_tl } \\
}{
  \vspace{0.5cm}
}

% At the end of the document, iterate over `\g_journal_out_seq` and write every entry
% into a line
\AtEndDocument{
  \iow_open:Nn \g_journal_stream { \c_sys_jobname_str.jrn }
  \seq_map_inline:Nn \g_journal_out_seq {
    \iow_now:Nn \g_journal_stream { #1 }
  }
  \iow_close:N \g_journal_stream
}

\ExplSyntaxOff

\makeatletter
\readjournaldates
\makeatother
% ---------------
\begin{document}
\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[2]
\end{newday}
\begin{newday}
  \lipsum[3]
\end{newday}
\end{document}

For example, let's call the two arrays \years and \entriesperyear, and let's say that a total of four days were inputed, three of which were in 2018 and one in 2019. Then it should be:

\years(1) = 2018, \years(2) = 2019, \entriesperyear(1) = 3, \entriesperyear(2) = 1

noibe
  • 2,084

1 Answers1

5

This answer does not really use an array, but a property list. This is an expl3 data structure which allows the entries to be iterated, using \prop_map_inline:Nn, or an individual entry to be accessed, using \prop_get:NnN. As a demonstration, we will print a list with the number of entries in every year at the end of the journal:

\documentclass{tufte-book}
\usepackage{datenumber, ifthen, xparse}
\usepackage{lipsum}
\setstartyear{2000}
\newcounter{day}

\ExplSyntaxOn

\cs_generate_variant:Nn \prop_get:NnNF { NfNF }
\cs_generate_variant:Nn \prop_gput:Nnn { Nff }
% Declare variables
\seq_new:N \g_journal_seq
\seq_new:N \g_journal_out_seq
\iow_new:N \g_journal_stream
\tl_new:N \l_journal_date_tl
\prop_new:N \g_journal_years_prop

% At the beginning of the run, read the lines of the `.jrn` file into a sequence.
% These are the dates. If the file can not be opened, it probably does not exists
% and we treat it as empty.
\cs_new:Npn \readjournaldates {
  \ior_open:NnT \g_journal_stream { \c_sys_jobname_str.jrn } {
    \ior_map_inline:Nn \g_journal_stream {
      \seq_gput_right:Nn \g_journal_seq { ##1 }
      \prop_get:NfNF \g_journal_years_prop { \tl_head:n { ##1 } } \l_tmpa_tl {
        \tl_set:Nn \l_tmpa_tl { 0 }
      }
      \prop_gput:Nff \g_journal_years_prop { \tl_head:n { ##1 } } {
        \int_eval:n { \l_tmpa_tl + 1 }
      }
    }
    \ior_close:N \g_journal_stream
  }
}

% The main environment:
\NewDocumentEnvironment{ newday }{ O{} }{
  \stepcounter{day}
  % If the sequence \g_journal_seq is not empty yet, then we already saved a date
  % for the current day. Save this day in `\l_journal_date_tl` and delete it from
  % the sequence. Otherwise we have not saved anything yet, so we choose the current
  % date.
  \seq_gpop_left:NNF \g_journal_seq \l_journal_date_tl {
    \tl_set:Nx \l_journal_date_tl { { \int_use:N \c_sys_year_int } \datedayname,~\today}
    \prop_get:NfNF \g_journal_years_prop { \int_use:N \c_sys_year_int } \l_tmpa_tl {
      \tl_set:Nn \l_tmpa_tl { 0 }
    }
    \prop_gput:Nff \g_journal_years_prop { \int_use:N \c_sys_year_int } {
      \int_eval:n { \l_tmpa_tl + 1 }
    }
  }
  % Now we have to save the choosen date for the next run. First, only store it in the
  % sequence `\g_journal_out_seq`, we only write it to the file at the end to avoid
  % overwriting the file if something fails:
  \seq_gput_right:NV \g_journal_out_seq \l_journal_date_tl
  \textbf{ #1 }
  \marginnote{ \tl_tail:N \l_journal_date_tl } \\
}{
  \vspace{0.5cm}
}

% At the end of the document, iterate over `\g_journal_out_seq` and write every entry
% into a line
\AtEndDocument{
  \iow_open:Nn \g_journal_stream { \c_sys_jobname_str.jrn }
  \seq_map_inline:Nn \g_journal_out_seq {
    \iow_now:Nn \g_journal_stream { #1 }
  }
  \iow_close:N \g_journal_stream
}

\ExplSyntaxOff

\makeatletter
\readjournaldates
\makeatother
% ---------------
\begin{document}
\begin{newday}[A day]
  \lipsum[1]
\end{newday}

\begin{newday}[Yet another day]
  \lipsum[1]
\end{newday}
\begin{newday}
  Today I couldn't even think about a title.
\end{newday}

\begin{newday}[Yet another day]
  Some very interesting text.
\end{newday}

\ExplSyntaxOn
\prop_map_inline:Nn \g_journal_years_prop {
  During~#1~there~have~been~#2~entries.~
}

% If you really want two "arrays"
\seq_new:N \g_journal_years_seq
\seq_new:N \g_journal_yearentries_seq
\prop_map_inline:Nn \g_journal_years_prop {
  \seq_gput_right:Nn \g_journal_years_seq { #1 }
  \seq_gput_right:Nn \g_journal_yearentries_seq { #2 }
}
\cs_new:Npn \years #1 {
  \seq_item:Nn \g_journal_years_seq { #1 }
}
\cs_new:Npn \entriesperyear #1 {
  \seq_item:Nn \g_journal_yearentries_seq { #1 }
}
\ExplSyntaxOff

In \years{2} we got \entriesperyear{2} entries.
\end{document}

enter image description here

  • Using \prop_map_inline and \prop_get, how can I make two expandable commands that emulate an array data structure? I'm asking this because I need to perform calculations with them. – noibe Jun 21 '19 at 21:27
  • @noibe See the edit. You could implement it directly, but that would become ugly. So here we actually generate your sequences. I also implemented you \years(...) syntax, even if I would recommend using regular braces instead. – Marcel Krüger Jun 21 '19 at 21:45
  • I used that syntax just because that's the one arrayjobx, which is what I've been using for creating and managing arrays, implements, but I too think regular braces are better. – noibe Jun 21 '19 at 22:10
  • How can I go back to that syntax? – noibe Jun 21 '19 at 22:13
  • @noibe I changed it. Basically the parens around #1 after \cs_new:Npn told TeX to look for an argument delimited by parens. Without them, TeX defaults to it's normal scanning rules: A single token or something between braces. So we just had to delete the parens. – Marcel Krüger Jun 21 '19 at 22:19
  • Oh, didn't know that. I tried to change the ( and ) after \cs_new:Npn into { and }, and it obviously failed. – noibe Jun 21 '19 at 22:33