2

I want to retrieve a member of a non-numeric array passed to a TikZ pic as an argument, to use it as a node label. \foreach easily does it while iterating over the array, but I cannot do it for the individual elements. The following are my two attempts that fail miserably:

\documentclass[tikz]{standalone}

\begin{document}

\tikzset{ pics/mypic/.style n args={1}{ code={ \foreach \a [count=\i] in {#1}{ \node at (\i, 1) {$\a$}; }

     % Attempt 1: Doesn't work
     % \node at (1, 2) {$#1[0]$};

     % Attempt 2: Doesn't compile
     % Error: Undefined control sequence. ... Missing $ inserted. ...
     \node at (2, 2) {$\pgfmathparse{{#1}[0]}\pgfmathresult$};
  }

} }

\tikz{\pic {mypic={\mathtt{a},\mathtt{b},\mathtt{c}};}} % \tikz{\pic {mypic={a,b,c};}} % It's the same for this one. % \tikz{\pic {mypic={0,1,2};}} % Attempt 2 works with this one, % \tikz{\pic {mypic={0,a,2};}} % but not with this one ("Unknown function `a').

\end{document}

Output:

bad array access

I want to be able to somehow draw a node with the label #1[0]. Regarding Attempt 2, I noticed that it yields the same errors also with the text arrays. It works fine for the all-numeric arrays, but hybrid arrays are also troubling, even if the accessed element is numeric.

  • Try https://gist.github.com/moewew/2e0fcd582432a4ec72360f17085443d2. The non-numeric example entries in the array from p. 1030 are quoted with "...", and that appears to be required to avoid blow-ups (see also p. 1026). There also appeared to be a scoping issue if \pgfmathparse is used too early/too far away from \pgfmathresult. I assume that \pgfmathparse/\pgfmathresult is also used internally, so you have to be careful that your use does not get overwritten. – moewe Jul 05 '20 at 13:51
  • Incidentally the answer to https://tex.stackexchange.com/q/134819/35864 also works here if you make sure that you use numbers where you need them: https://gist.github.com/moewew/52fd825cfdd491656ab3248eda6e6f66 (the important bit is accessing the array element via evaluate=\x as \myvar using ({\radi[\x]}) and then \myvar that is unchanged). – moewe Jul 05 '20 at 13:51
  • Re \pgfmathresult being overwritten see also https://tex.stackexchange.com/q/497648/35864, https://tex.stackexchange.com/q/9988/35864, https://tex.stackexchange.com/q/510584/35864 – moewe Jul 05 '20 at 14:41
  • @moewe I don't like the "quotes" solution. Primarily because it requires me (and anybody else struggling at this point) to wrap their every single non-numeric array element with quotes, and they are (and might also be for anybody else) a lot at this point. It also convolutes the \foreach statement using the array. Finally, it's hard to accept that it cannot be done without the quotes, when un-quoted non-numeric elements can be captured/stored by the \foreach's iteration variable just fine. – Utkan Gezer Jul 20 '20 at 20:22
  • I may very well be wrong, but my reading of the TikZ manual is that non-numeric input in arrays is only supported if the string is wrapped in "...". I suspect some things may work without quotes, but that would be accidental and not something you can in general rely on. But I no next to nothing about TikZ so take that with a grain of salt. – moewe Jul 20 '20 at 20:24
  • @moewe I just posted an answer that makes use of \foreach. I see that the TikZ & PGF Manual explicitly states some examples iterating over arrays with un-quoted text and math. I don't think that solution relies on accidents. – Utkan Gezer Jul 21 '20 at 10:49
  • Ah, there seems to be a difference between the lists that \foreach iterates over and the arrays that are evaluated with \pgfmathparse etc. The former may contain more or less arbitrary content but cannot be accessed via [i]. The latter may only contain stuff that is valid in the sense of §95.2 of the manual, but elements can be accessed with[i]. Apart from additional curly braces the two look the same for one-dimensional numerical values, but for non-numeric values arrays need "...". – moewe Jul 21 '20 at 16:09
  • Your question and the answers here use the list convention of \foreach, but the question https://tex.stackexchange.com/q/134819/35864 uses arrays (and your title mentions arrays). – moewe Jul 21 '20 at 16:10

2 Answers2

2

The following command uses \foreach to retrieve the element at a given index, and assign it globally to the given macro.

% Does #1 = #2[#3] with 0-based indexing
\newcommand\arrayget[3]{
    \foreach \a [count=\i from 0] in {#2}{
        \ifnum #3=\i
            \xdef#1{\unexpanded\expandafter{\a}}
        \fi
    }
}

References for the curious:

  • Global definition with \xdef: source 1
  • \unexpanded\expandafter{...} to expand \a once: source 2

It should be able to handle anything that \foreach can. Demonstration:

\documentclass[tikz]{standalone}

\newcommand\arrayget[3]{ % Does #1 = #2[#3] with 0-based indexing \foreach \a [count=\i from 0] in {#2}{ \ifnum #3=\i \xdef#1{\unexpanded\expandafter{\a}} \fi } }

\begin{document}

\tikzset{ pics/mypic/.style n args={1}{ code={ \foreach \a [count=\i] in {#1}{ \node at (\i, 1) {$\a$}; }

     \arrayget{\asd}{#1}{0} \node at (1.5, 2) {$\asd$};
     \arrayget{\asd}{#1}{1} \node at (2.0, 2) {$\asd$};
     \arrayget{\qwe}{#1}{2} \node at (2.5, 2) {$\qwe$};
  }

} }

\tikz{\pic {mypic={\mathtt{a},\mathtt{b},\mathtt{c}};}} \tikz{\pic {mypic={a,b,c};}} \tikz{\pic {mypic={0,1,2};}} \tikz{\pic {mypic={0,a,2};}}

\end{document}

Output:

Properly indexed

1

Here, I use listofitems to read the desired data into an array \mydata. The OP suggested a useful EDIT to my original approach by including all of the listofitems processing inside the \tikzset code.

Note that the 2 in \mydata[2], calling out a single array element, can be any expandable calculation that results in an integer. Also, negative values denote "reverse index", that is, relative to the end of the array, rather than the beginning.

\documentclass[tikz]{standalone}
\usepackage{listofitems}

\begin{document}

\tikzset{ pics/mypic/.style n args={1}{ code={ \foreach \a [count=\i] in {#1}{ \node at (\i, 1) {$\a$}; }

     \readlist\mydata{#1}
     \node at (2, 2) {$\mydata[2]$};
  }

} }

\tikz{\pic {mypic={\mathtt{a},\mathtt{b},\mathtt{c}};}} \end{document}

enter image description here

  • 1
    I think it does fit! However, I don't like the idea of having to make the \readlist call each time before drawing mypic. How about making that call in the pic's code instead? That way, we also get to also keep our document just as it was. – Utkan Gezer Jul 21 '20 at 11:23
  • @UtkanGezer Thank you for your helpful edit. – Steven B. Segletes Jul 21 '20 at 11:29
  • Two valid answers within the same hour, and to a question 22 days old! I picked this one as the answer, as I will not be maintaining my hacky command in the other one, but the package listofitems sure might be. – Utkan Gezer Jul 21 '20 at 12:11