I have a clist with the content, {9.8,5.1,10.1b,1.3,1.4,5,5.1.1,8a,9.7,9.9,10.1a,10.1c,1.2,8b,8c}. How can I generate sorted and compressed output from this using expl3 which might look like:

1.2--1.4, 5, 5.1.1, 8a--8c, 9.7--9.9 and 10.1a--10.1c

Here is a minimal example:





  • Please provide a Minimum Working Example. A mere fragment of a fragment of code is not terribly useful to anybody trying to help you. – cfr Jul 11 '16 at 8:32
  • Sorry, I have added a minimal example. – CV Radhakrishnan Jul 11 '16 at 13:46
  • How flexible does the code need to be with non-number parts? E.g., must it deal with things like 10.4x.9C even 7.C ? What about 7.b! (i.e., punctuation) ? – Will Robertson Jul 12 '16 at 7:07
  • @will these are counter numbers of various objects in a document like sections, equations, floats and the like. When multiple numbers of these are to be cross-linked in a location, sorting and compression have to be applied. Usually, alpha comes only as the last bit and there are no punctuations except the dot separator. The alphabet and the preceding numeral have no dot separator. – CV Radhakrishnan Jul 13 '16 at 7:57

A first version, the compression works now, using l3regex features.

There are two basic difficulties:

1.) Splitting the entries into a numerical part like a 24.345345.34 and a and alphabetical tail --> use \regex_extract_all:Nnn for this. 2.) Using a sensible matching 'algorithm' used for \seq_sort:Nn

The basic idea is to convert something like 8.5.3.d into 8.534sinced` is the 4th character in the normal Latin alphabet.

However this, is quite tricky, as the subsequent numbers like .534 are quite large -- using this without are would lead to carry of numbers to the leading number.

Binning the range and compressing is also a little bit awkward.

Update: A previous version used the l3sort package -- this package is considered deprecate and its features are absorbed by expl3.




\seq_new:N \l_item_rounded_seq
\seq_new:N \l_sort_abundance_seq
\seq_new:N \l_alpha_extract_seq

\seq_new:N \l_sort_seq
\seq_new:N \g_sortandcompress_seq
\fp_new:N \l_sort_varone_fp
\fp_new:N \l_sort_vartwo_fp

\cs_generate_variant:Nn \seq_set_split:Nnn {Nnx}
\cs_generate_variant:Nn \int_from_alph:n {V}
\cs_generate_variant:Nn \regex_extract_all:nnN {nxN}
\cs_generate_variant:Nn \regex_split:nnN {nxN}

  \regex_split:nnN { [a-z] } { #1 } \l_tmpb_seq
  \regex_extract_all:nnN { [^0-9.] } { #1 } \l_alpha_extract_seq
  \seq_set_split:Nnx \l_tmpa_seq {.} { \seq_item:Nn \l_tmpb_seq {1}}
  \fp_zero:N #2
  \fp_set:Nn \l_tmpb_fp {1.0}
  \int_set:Nn \l_tmpa_int {0}
  \fp_set:Nn \l_tmpa_fp {1.0}
  \seq_map_inline:Nn \l_tmpa_seq {%
    \tl_set:Nx \l_tmpa_tl {##1} 
    \int_incr:N \l_tmpa_int
    \int_compare:nNnTF { \l_tmpa_int } = {1} {%
      \int_set:Nn \l_tmpb_int {0}
      \int_add:Nn \l_tmpb_int {\tl_count:N \l_tmpa_tl}
    \fp_set:Nn \l_tmpa_fp {\fp_eval:n {0.1 ^ {\l_tmpb_int}}}
    \fp_add:Nn #2 { ##1 * \l_tmpa_fp}
  % Now convert the possibly trailing alpha character
  \int_compare:nNnT {\seq_count:N \l_alpha_extract_seq } > {0} {%
    \tl_set:Nx \l_tmpa_tl {\seq_item:Nn \l_alpha_extract_seq {1}}
    \int_set:Nn \l_tmpa_int  {\int_from_alph:V { \l_tmpa_tl}}
    \tl_set:NV  \l_tmpa_tl {\l_tmpa_int}
    \int_add:Nn \l_tmpb_int {\tl_count:N \l_tmpa_tl}% How many characters does the converted alpha need? -> power of ... 
    \fp_set:Nn \l_tmpa_fp {\fp_eval:n {0.1 ^ {\l_tmpb_int}}}
    \fp_add:Nn #2 { \l_tmpa_int * \l_tmpa_fp }

  \seq_set_from_clist:Nn \l_sort_seq {#1}%
  \seq_sort:Nn \l_sort_seq {%
    \fp_compare:nNnTF {\l_sort_varone_fp} > { \l_sort_vartwo_fp }
    { \sort_reversed:}
    { \sort_ordered:}
  %\seq_use:Nn \l_sort_seq {,\par}


  \seq_gclear:N \g_sortandcompress_seq 

  \seq_clear:N \l_item_rounded_seq
  \seq_map_inline:Nn \l_sort_seq {%
    \seq_put_right:Nx \l_item_rounded_seq {\fp_eval:n {floor (\l_sort_varone_fp , 0)}} % Round to the smaller integer

  \seq_set_eq:NN \l_sort_abundance_seq \l_item_rounded_seq 
  \seq_remove_duplicates:N \l_sort_abundance_seq

  \seq_clear:N \l_tmpb_seq
  \seq_clear:N \l_tmpa_seq

  \seq_map_inline:Nn \l_sort_abundance_seq {%
    \int_zero:N \l_tmpa_int 
    \int_zero:N \l_tmpb_int 
    \seq_map_inline:Nn \l_item_rounded_seq {%
      \int_compare:nNnT { ##1 } = { ####1 } { \int_incr:N \l_tmpb_int }
    \seq_map_inline:Nn \l_item_rounded_seq {%
      \int_incr:N \l_tmpa_int 
      \int_compare:nNnT { ##1 } = { ####1 } { \seq_map_break: }
    \seq_put_right:NV \l_tmpa_seq {\l_tmpa_int} % store the current start index to \l_tmpa_seq 
    \seq_put_right:NV \l_tmpb_seq {\l_tmpb_int} % store the number of elements to \l_tmpb_seq 

  % Let's determine the real entries now
  \int_zero:N \l_tmpa_int 
  \seq_map_inline:Nn \l_sort_abundance_seq {%
    \int_incr:N \l_tmpa_int
    \int_set:Nn \l_tmpb_int {\seq_item:Nn \l_tmpb_seq{ \l_tmpa_int} }
    \int_compare:nNnTF { \seq_item:Nn \l_tmpb_seq{ \l_tmpa_int} }  = {1} {%
      \seq_gput_right:Nx \g_sortandcompress_seq {\seq_item:Nn \l_sort_seq {\seq_item:Nn \l_tmpa_seq {\l_tmpa_int}}}
      % Calculate the stop index
      \int_set:Nn \l_tmpb_int {\seq_item:Nn \l_tmpa_seq {\l_tmpa_int} + \seq_item:Nn \l_tmpb_seq {\l_tmpa_int} - 1 }
      \seq_gput_right:Nx \g_sortandcompress_seq {\seq_item:Nn \l_sort_seq {\seq_item:Nn \l_tmpa_seq {\l_tmpa_int}} -- \seq_item:Nn \l_sort_seq {\l_tmpb_int}}
  \seq_use:Nn \g_sortandcompress_seq {\par}




I don't expect that this will work always. Entries like 2.2.a or 1.a.5 etc. will fail.

enter image description here

  • This works fine. The only remaining part is compression. – CV Radhakrishnan Jul 11 '16 at 14:17
  • @CVRadhakrishnan: I had no time so far – user31729 Jul 11 '16 at 15:40
  • @CVRadhakrishnan: See the update, please – user31729 Jul 12 '16 at 8:55
  • I could not find any problems in my preliminary tests except for the segments with alpha characters. for eg., "8a--8b" needs to be "8a, 8b" and "100.234b--100.467q" shall be "100.234b, 100.4671". Barring these, it looks OK. I will have thorough testing and will get back if I find any other problems. – CV Radhakrishnan Jul 13 '16 at 7:42
  • @CVRadhakrishnan: Oh yes, I overlooked that requirement for the ` a, ...,b` references – user31729 Jul 13 '16 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.