# Create an array of variables

I'm creating a package for my question papers. (I'm aware of the exam class but I don't want to use that.) I have everything ready but I want to create a grading table at the top of the first page. For that, I need to put the exam points in an array. I have a command that is called for outputting the score. What I need to do is put them in some array-like variable so that I can then loop over it and output the points in a table. How can I create that array? I have no idea. Any hints would be highly appreciated.

-
I would think in terms of a list structure and possibly a table. Would tex.stackexchange.com/questions/19746/cunning-latex-tricks/… form a basis of what you want? Can you define better what you mean by array like variable? I guess you mean a like a row in a table? –  Yiannis Lazarides Dec 7 '11 at 13:46
By now you probaly already have an answer that works for you, but this is what I did when I wanted to do the same in my exam template: bitbucket.org/mickesv/templates/overview (You'll find it in the ExamTemplate directory). I probably went overboard and wrote the question points into the .aux-file, and create the table on the next compile round, but the output is probably close to what you would like. –  user14438 May 10 '12 at 8:25

You can define a macro which name contains numbers. Use LaTeX2e's \@namedef and \@nameuse, or \csdef, \csuse from etoolbox package. And etoolbox itself provides list operations without index.

An example:

\documentclass{article}
\usepackage{etoolbox}

\newcounter{cnt}
\newcommand\textlist{}
\newcommand\settext[2]{%
\csdef{text#1}{#2}}
\stepcounter{cnt}%
\csdef{text\thecnt}{#1}}
\newcommand\gettext[1]{%
\csuse{text#1}}

\newcounter{colnum}
\newcommand\maketabularrow[1]{%
\setcounter{colnum}{0}%
\whileboolexpr
{ test {\ifnumcomp{\value{colnum}}{<}{#1}} }%
{&\stepcounter{colnum}\thecolnum}
}

\begin{document}

\settext{100}{one hundred}

This is text \gettext{1} and \gettext{3}, that is text \gettext{2}.

\begin{tabular}{ |c@{} *{\thecnt}{c|} } % the first row is hidden
\hline
\maketabularrow{\thecnt}\\ \hline
\end{tabular}

100 is \gettext{100}.

\end{document}

-
Well, try to do it inside a tabular environment! (I did, and it won't work - and the results are, well, weird. I don't have time to examine this problem now, I guess it might be connected with expansion timing.) –  mbork Dec 7 '11 at 12:29
@mbork: Indeed, expansion makes it difficult to make a tabular in a loop. You must design the tabular first and pass the list through argument. I'll add an example. –  Leo Liu Dec 7 '11 at 12:53
Interesting (and fairly complicated). I will try it out and post follow up comments. –  recluze Dec 7 '11 at 13:02
@recluze: Yes, it is quite complicated if you want to make a tabular in a loop. However, it is much easier to put several tabulars (boxes) together — it looks like a single tabular. –  Leo Liu Dec 7 '11 at 13:05
@LeoLiu: thanks, I was thinking about something like that. I guess it /might/ be done in a simpler way, but I'm not sure. Also, I am pretty sure that a clean solution using luatex exists. –  mbork Dec 7 '11 at 13:23

You have two questions that I can see. First, you want to know how to create an associative (programming) array mapping questions to their points. And second, you want to know how to create a (LaTeX table) array that prints these. As I usually do, I recommend using pgfkeys as your programming language here, particularly (in this case) because as a key-value package, its entire life is devoted to constructing associative arrays.

Here's how I'd create your array of points:

\usepackage{pgfkeys}
\pgfkeys{
/points array/.is family, /points array,
.unknown/.style = {\pgfkeyscurrentname/.initial = #1},
}

\newcommand\questionhaspoints[1]{\pgfkeys{/points array, #1}}
\newcommand\getquestionpoints[1]{\pgfkeysvalueof{/points array/#1}}


You can then write, say, \questionhaspoints{1 = 10, 2 = 5} to say that question 1 has 10 points and question 2 has 5 points. If you want to retrieve these, you just call \getquestionpoints{1} and \getquestionpoints{2}.

The \pgfkeys invocation sets up the family /points array so that any time you try to assign a new key in it (one that is "unknown" as of yet) it is simply filled with the value you requested. pgfkeys can do lots of stuff with keys other than just store their values, as you will see in a second.

To construct the LaTeX array, I think it is necessary to loop and construct the rows before calling \begin{tabular}, since in TeX, alignments are very particular about what can appear where in them, in particular the & and \\ directives. It's best for everything to look "right" before starting the table.

Here is my code for constructing the table:

\usepackage{pgffor}
\pgfkeys{
/points array,
table/.append = {
Question #1
&
\getquestionpoints{#1}
\\
},
},
}

\newcommand\makepointstable[1]{%
\pgfkeys{
/points array,
table/.initial = {},
}%
}


The new key /points array/add to table just tacks its argument onto a key called table (presumed to be in /points array too). It is used in the loop that is implied by the construction add to table/.list = {<list of question numbers>}, which performs a loop over the numbers in its argument, calling add to table with that number each time. The result is that the table key holds the body of the array. Now you can put that in a {tabular}. Here is a complete document that does that:

\documentclass{article}
\usepackage{pgfkeys,pgffor}

\pgfkeys{
/points array/.is family, /points array,
.unknown/.style = {\pgfkeyscurrentname/.initial = #1},
table/.append = {
Question #1
&
\getquestionpoints{#1}
\\
},
},
}

\newcommand\questionhaspoints[1]{\pgfkeys{/points array, #1}}
\newcommand\getquestionpoints[1]{\pgfkeysvalueof{/points array/#1}}

\newcommand\makepointstable[1]{%
\pgfkeys{
/points array,
table/.initial = {},
}%
}

\begin{document}
\questionhaspoints{1 = 10, 2 = 8, 3 = 15, 4 = 10}
\makepointstable{4}

\begin{tabular}{l|r}
\pgfkeysvalueof{/points array/table}
\end{tabular}
\end{document}

-

With the spreadtab package, you can set up a tabular environment that has some basic spreadsheet functionality. This should be enough to sum your exam points.

I don't have time to work up an example now. It's lunch time! I'll be back later…

-
Thanks for pointing to this package - it looks promising! –  mbork Dec 7 '11 at 12:29
Thanks for the package link. It's very interesting but I don't see how it will be relevant to my problem. My situation is simpler but different. –  recluze Dec 7 '11 at 13:03
@recluze then I don't understand what you want. Could you add an example that shows how you would like things to work? What do you mean by a "grading table"? –  Seamus Dec 7 '11 at 13:57

Working on the answer by Leo Liu above, here's what works for a grading table:

\documentclass{article}
\usepackage{etoolbox}

\newcounter{cnt}
\newcommand\textlist{}
\newcommand\settext[2]{%
\csdef{text#1}{#2}}
\stepcounter{cnt}%
\csdef{text\thecnt}{#1}}
\newcommand\gettext[1]{%
\csuse{text#1}}

\begin{document}

This is text \gettext{1} and \gettext{3}, that is text \gettext{2}.

\begin{tabular}{ |c@{} *{\thecnt}{c|} } % the first row is hidden
\hline
\newcounter{colnum}%
\setcounter{colnum}{0}
\whileboolexpr
{ test {\ifnumcomp{\value{colnum}}{<}{\thecnt}} }%
{&\stepcounter{colnum}\gettext{\thecolnum}}\\
\hline
\setcounter{colnum}{0}
\whileboolexpr
{ test {\ifnumcomp{\value{colnum}}{<}{\thecnt}} }%
{&\stepcounter{colnum}\hspace{1cm}}\\\hline
\end{tabular}

\end{document}


The output is this:

-

The latest version of etoolbox provides lists and their management options. Here is the solution for any one interested.

The below code demonstrates how easy it is now to create and iterate over a list of variables.

\documentclass{article}
\RequirePackage{etoolbox} % defines lists and their operations
\RequirePackage{tabulary} % defines content-based sizable tables

\begin{document}

% define few variables that hold some value
\def\One{this is one}
\def\Two{this is two}
\def\Three{this is three}

% add the above commands to a list named CmdList'

% Now loop-over them to print their values
\begin{description}
\renewcommand*{\do}[1]{\item[#1:] \csuse{#1}}
\dolistcsloop{CmdList}
\end{description}

% creating a table needs little bit more trickery; You cannot insert table entries directly.
% You first have to accumulate all table data into some temporary variable and then use that.
\begingroup
\newcommand\tablecontent{}
\def\do#1{\appto\tablecontent{\hline \textbf{#1} & \csuse{#1}\\}}%
\dolistcsloop{CmdList} % collect the data in a table format
\begin{tabulary}{\textwidth}{|L|L|} % now print the collected data
\tablecontent \hline
\end{tabulary}
\endgroup

\end{document}
`

-