2

I'm feeding a LaTeX template with content through an external script, and the last formatting step that's resisting me is this one: a macro \equispace[6]{a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t} that would turn the arguments into an array of this kind,

enter image description here

where the array extends to the full linewidth, the groups of items are equi-spaced and centred about their own group. I tried something with tabularx and also with minipage, but this is proving trickier than my limited TeX skills allow. Note that I know in advance how many items are in each group, so I could use a hard-coded template if there is a good way to parse the contents.

6
  • 1
    always 6 groups or variable what layout is \equispace[7]... supposed to do? (and as it is an optional argument what default value? Mar 7, 2018 at 20:58
  • And should it always be 3 columns?
    – Skillmon
    Mar 7, 2018 at 21:05
  • sorry I realise the [6] is pretty meaningless here, in my head it was reminiscent of the \newcommand syntax.
    – user156626
    Mar 7, 2018 at 21:08
  • the number of columns (and items) is fixed indeed
    – user156626
    Mar 7, 2018 at 21:08
  • And I just started to develop something that works for an arbitrary number of items :(
    – Skillmon
    Mar 7, 2018 at 21:09

3 Answers 3

1

Ignoring the [6] argument (see comment under the question) then

enter image description here

\documentclass{article}

\newcommand\equispace[6]{%
\begin{center}%
\begin{tabular}{@{}c@{}}\zz{#1}\\\zz{#4}\end{tabular}\hfill
\begin{tabular}{@{}c@{}}\zz{#2}\\\zz{#5}\end{tabular}\hfill
\begin{tabular}{@{}c@{}}\zz{#3}\\\zz{#6}\end{tabular}%
\end{center}%
}

\makeatletter
\def\zz#1{\@for\z:=#1\do{\z\,}}
\makeatother

\begin{document}

\equispace{a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}

\end{document}
4
  • oh actually I permuted the groups from the order that you asked for, but just move #1 ... #6 in the definition to change the order. Mar 7, 2018 at 21:04
  • If it's a fixed number, why not use one tabular* instead?
    – Skillmon
    Mar 7, 2018 at 21:10
  • very neat, thank you very much! I pictured a strategy along those lines, but the exact syntax would have have eluded me for weeks.
    – user156626
    Mar 7, 2018 at 21:10
  • @Skillmon i was "hedging my bets` if the answer to my question under the original question had been that an argument to support arbitrary number of groups would be needed, then it's easier to extend this than to generate a single table body and fight over & appearing in the right places. Mar 7, 2018 at 21:16
1

If you run an external script, it could output something like the following:

\begin{tabular*}{\textwidth}{@{}c@{\extracolsep{\fill}}c@{\extracolsep{\fill}}c@{}}
  a b c &   g h   & m n o\\
  d e f & i j k l & p q r s t
\end{tabular*}

Output: enter image description here

With lines:

\begin{tabular*}{\textwidth}{|@{}c@{\extracolsep{\fill}}c@{\extracolsep{\fill}}c@{}|}
  \hline
  a b c &   g h   & m n o\\
  \hline
  d e f & i j k l & p q r s t\\
  \hline
\end{tabular*}

enter image description here

EDIT:

The following should be able to parse an arbitrary number of arguments (and orders it like in your example). The code might not be perfect and could perhaps be enhanced, but it did work on everything I've thrown at it so far.

\documentclass[border=2mm]{standalone}

\makeatletter
\newcount\equi@columns
\newcount\equi@rows
\newcount\equi@args
\newcount\equi@currow
\newcount\equi@curcol
\newcommand\equispace*{}
\def\equispace%
  {%
    \equi@currow=\@ne
    \equi@curcol=\@ne
    \@ifnextchar<
      {\equispace@i}
      {\equispace@i<3>}%
  }
\def\equispace@i<#1>%
  {%
    \equi@columns=#1
    \@ifnextchar[
      {\equispace@ii}
      {\equispace@ii[6]}%
  }
\def\equispace@ii[#1]%
  {%
    \equi@args=#1
    \equi@rows=\numexpr\equi@args/\equi@columns\relax
    \ifnum\numexpr\equi@rows*\equi@columns<\equi@args
      \advance\equi@rows by \@ne
    \fi
    \equi@readarg
  }
\newcommand\equi@readarg[1]
  {%
    \advance\equi@args by \m@ne
    \expandafter\equi@addto@row\expandafter
      {\csname equi@row@\the\equi@currow\endcsname}{\equi@format{#1}}%
    \advance\equi@currow by \@ne
    \ifnum\equi@currow>\equi@rows
      \equi@currow=\@ne
      \advance\equi@curcol by \@ne
    \fi
    \ifnum\equi@args=0
      \expandafter\equi@output
    \else
      \expandafter\equi@readarg
    \fi
  }
\newcommand\equi@addto@row[2]
  {%
    \ifnum\equi@curcol=\@ne
      \def#1{#2}%
    \else
      \edef#1{\unexpanded\expandafter{#1&#2}}%
    \fi
  }
\newcommand\equi@addto@colspec[1]
  {\edef\equi@colspec{\unexpanded\expandafter{\equi@colspec#1}}}
\newcommand*\equi@output
  {%
    \def\equi@colspec{@{}c}%
    \equi@args=\@ne
    \loop\ifnum\equi@args<\equi@columns
      \advance\equi@args by \@ne
      \equi@addto@colspec{@{\extracolsep{\fill}}c}%
    \repeat
    \equi@addto@colspec{@{}}%
    \equi@currow=0
    \def\equi@body{}%
    \loop
      \advance\equi@currow by \@ne
      \expandafter\equi@addto@body@expand\expandafter
        {\csname equi@row@\the\equi@currow\endcsname}%
    \ifnum\equi@rows>\equi@currow
      \equi@addto@body{\\}%
    \repeat
    \begin{tabular*}{\textwidth}{\equi@colspec}%
      \equi@body
    \end{tabular*}%
  }
\newcommand\equi@addto@body[1]
  {\edef\equi@body{\unexpanded\expandafter{\equi@body#1}}}
\newcommand\equi@addto@body@expand[1]
  {\expandafter\equi@addto@body\expandafter{#1}}
\newcommand\equi@format[1]
  {\@for\equi@tmp:=#1\do{\equi@tmp\,}}
\makeatother

\begin{document}
\equispace<5>[96]
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
  {a,b,c}{d,e,f}{g,h}{i,j,k,l}{m,n,o}{p,q,r,s,t}{u,v}{w}{x,y}{z}{1,2}{3,4}
\end{document}

The command \equispace takes two optional arguments, the first enclosed in <> is the number of columns and defaults to 3, the second in [] is the number of arguments and defaults to 6.

Output for 5 columns and 96 arguments: enter image description here

2
  • thanks! The layout is spot on; I'll probably need to ask another question to sort the parsing of values: the content is best provided in the form of one or two lists of characters, say a,b,c,g,h,m,n,o and d,e,f,i,j,k,l,p,q,r,s,t (for the curious, it resides in a YAML metadata file, which is meant to be as readable as possible and void of markup). I know how many items there are, so I'd need some way to parse those strings and fill the tabular template that you proposed.
    – user156626
    Mar 7, 2018 at 21:07
  • @user156626 see my edit for code that parses an arbitrary number of arguments. It always fills one column after the other. There is no check for too many columns, so 4 columns and 6 arguments result in an empty fourth column.
    – Skillmon
    Mar 7, 2018 at 22:34
0

listofitems to the rescue!

Uses the syntax: \equispace{a,b,c/d,e,f/g,h/i,j,k,l/m,n,o/p,q,r,s,t}

\documentclass{article}
\usepackage{listofitems,stackengine,pgffor}
\newcommand\mybox{{\fboxsep=-\fboxrule\fbox{\strut\rule{\linewidth}{0pt}}}}
\newcommand\equispace[1]{%
  \setsepchar[;]{/;,}%
  \readlist*\mylist{#1}%
  \noindent\leavevmode\rlap{\stackunder[-\fboxrule]{\mybox}{\mybox}}%
  ~\foreach\x in{1,3,...,\listlen\mylist[]}{%
    \ifnum\x=1\relax\else\hfill\fi%
    \def\stacktype{L}%
    \stackunder{\foreachitem\y\in\mylist[\x]{\ifnum\ycnt=1\else\ \fi\y}}%
      {\foreachitem\y\in\mylist[\numexpr\x+1]{\ifnum\ycnt=1\else\ \fi\y}}%
  }~%
}
\begin{document}
\equispace{a,b,c/d,e,f/g,h/i,j,k,l/m,n,o/p,q,r,s,t}
\end{document}

enter image description here

2
  • I like it! Thanks for the original alternative, the syntax is actually more convenient for my use case.
    – user156626
    Mar 8, 2018 at 4:26
  • @user156626 I find a number of cases for which parsing a single long argument is simpler than digesting a large number of separately enumerated arguments. For such cases, the listofitems package is really handy. Mar 8, 2018 at 11:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .