0

I would like to get the length of an arrajob array, something which the package sadly does not provide. Currently I have to manually adjust a length variable whenever I add or remove elements from an array, which can get annoying.

I am using LuaLaTeX; I wrote up the following code which seems like it should do the work

\documentclass{article}

\usepackage{arrayjob}
\usepackage{ifthen}
\usepackage{etoolbox}

\newarray\Test%

\readarray{Test}{%
    A & B & C%
}

\newcommand{\Length}[1]{
    \newboolean{done}
    \setboolean{done}{false}

    \directlua{i = 1}
    \whiledo{\NOT\boolean{done}}{

      \def\Iteration{\directlua{tex.print("\\#1(" .. i .. ")")}}

        \ifx\Iteration\empty
            \setboolean{done}{true}
      \fi
      
      %% \ifnum\directlua{tex.print(i)}=11
      %%   \setboolean{done}{true}
      %% \fi

      %% \directlua{tex.print(i .. " \\#1(" .. i .. ")")}


      \directlua{i = i + 1}
    }

    \directlua{tex.print(i)}
}


\begin{document}
    \Length{Test}
\end{document}

But instead it just hangs. Uncommenting the block at the end of the loop results in the output:

enter image description here

Showing that the parameters all seem to be correct, but \ifx does not recognize the string as empty. I have also tried comparing it to another macro of an empty array element, and tried \ifthanelse and etoolbox's \ifstrempty, but to no avail.

Is there some other, better way to find the length of an array? Alternatively, do you know what is the issue with the code above?

5
  • Forget arrayjob. By the way, with LaTeX you should use arrayjobx anyway. The work of the package can be more easily implemented in expl3.
    – egreg
    Oct 11 '21 at 10:32
  • It isn't clear why you are using this array package but \ifx\Iteration\empty can never be true as you have defined it on the line above with a definition that is not the same as the definition of \empty also beware adding white space, your code adds lots of paragraph ends and space tokens note the double space before B and C in your image Oct 11 '21 at 11:06
  • @egreg The problem persists with arrayjobx. I will look into expl3, but it seems to be far more primitive and its syntax is very non-latex-like, and unintuitive for me, much more so than arrayjob.I guess the package may simply not be meant to be used with a more dynamic list, but a simple length function would still be nice instead of learning new complicated syntax.
    – Atai Ambus
    Oct 11 '21 at 20:37
  • @DavidCarlisle I don't understand what you are saying. Shouldn't \empty be an emptty string and \Iteration be an empty string when i is out of bounds? I also tried to remove all whitespaces and blank lines, using % to make sure no whitespace slips, but to no avail. I'll edit the question tomorrow to provide an example.
    – Atai Ambus
    Oct 11 '21 at 20:43
  • ifx compares the token lists in the definition Iteration is \directlua{tex.print("\\#1(" .. i .. ")")} and empty is empty, these are compared without evaluation and they are visibly not the same. Oct 11 '21 at 21:18
3

I would recommend switching to expl3 instead. Not only does it provide sequences for this kind of stuff, it also allows inside of it's \ExplSyntaxOn/\ExplSyntaxOff blocks the use of spaces and newlines without generating actual spaces and paragraphs. Without it, you have to hide these in order not to add wrong spaces to your output.

Even if you want to stay with arrayjob, you probably want to call it as arrayjobx instead. This is a newer version which uses the command name \arrayx instead of \array since using \array overwrites LaTeX's built in {array} environment for matrices.

Then, there is a much easier and more reliable way to get the length of an array: arrayjobx defines for every array named ArrayName a macro \total@ArrayName containing the length. Of course, by default you can't enter \total@ArrayName directly since @ can't appear in a control sequence, so you need to use it between \makeatletter/\makeatother or access it though \csname. Since you want a name based wrapper anyway, \csname is the right tool:

\documentclass{article}

\usepackage{arrayjobx}
\usepackage{ifthen}
\usepackage{etoolbox}

\newarray\Test

\readarray{Test}{%
    A&B&C&&D%
}

\newcommand \Length [1] {%
  \begincsname total@#1\endcsname
}


\begin{document}
  \Length{Test}
\end{document}
0

As @DavidCarlisle said, this seems to be an issue of \ifx expansion.

\ifx\Iteration\empty
    \setboolean{done}{true}
\fi

I thought \ifx compares strings and would expand its arguments until it found a literal value, but really it compares tokens and expands exactly ones.

Whereas \empty expands to empty token \Iteration expands once to \directlua{tex.print("\\#1(" .. i .. ")")}, which is of course not an empty token.

My solution was to run arrayjob's \checkArrayName that puts the value into \cacheddata. Then I used \cacheddata which expands once to the correct value.

I also needed to fix two small bugs, one was to only increment if the loop was not done and one two take one off the final i, since the loop finishes out of bounds (and arrayjob is 1-indexed).

\documentclass{article}

\usepackage{arrayjob}
\usepackage{ifthen}

\newarray\Test

\readarray{Test}{
    A & B & C
}

\newcommand{\Length}[1]{
    \newboolean{done}
    \setboolean{done}{false}

    \directlua{i = 1}
    \whiledo{\NOT\boolean{done}}{

      \directlua{tex.print("\\check#1(" .. i ..")")}

      %% \directlua{tex.print(i .. " \\cachedata")}

      \ifx\cachedata\empty
            \setboolean{done}{true}
      \else
        \directlua{i = i + 1}
      \fi


    }

    \directlua{tex.print(i - 1)} % Last i is already out of bounds
}


\begin{document}
    \Length{Test}
\end{document}

There is probably a more elegant way to fix the original problem in the code using some tex primitives to control expansion, but the above code does answer the original problem correctly.

Uncommenting the commented line to see a bit more udner the hood:

enter image description here

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.