4

I am trying to parse arrays of strings, but when I create an array of containing a single string (say the string contains n characters), it is not parsed correctly, instead it's parsed as containing n items, with each item an individual character. can anyone shed some light on why does this happen? thanks in advance! Code and output below:

\documentclass{article}
\usepackage{pgfmath,pgffor}
\begin{document}

\noindent Array 1 is \{"this","is","cool"\}

\def\myArray{{"this","is","cool"}}

\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\pgfmathparse{\myArrayLength-1}
\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}

\noindent Array 2 is \{"not cool"\}

\def\singleItemArray{{"not cool"}}

\pgfmathparse{dim(\singleItemArray)}
\edef\singleItemArrayLength{\pgfmathresult}

\pgfmathparse{\singleItemArrayLength-1}
\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 2, item \i: \pgfmathparse{\singleItemArray[\i]}\pgfmathresult \newline
}
\end{document}

enter image description here

  • 1
    Interestingly, there is no formal definition for arrays, just a few examples. It's not the space, because {{"notcool"}} produces the same issue. Length one arrays seem to be invalid, as {{12}} turns out to have length two as well. – egreg Oct 21 '17 at 22:38
  • To work around this bug/feature, you can enclose each element into braces: \def\myArray{{{"this"},{"is"},{"cool"}}}, \def\myArray{{{"not cool"}}}... – Paul Gaborit Oct 22 '17 at 7:29
1

I found a solution with an other array length command and some if...else statements to handle the single item arrays, which are causing problems.

Step 1: Compute the size of an array with code by @Alain Matthes

\def\getlen#1{%
\pgfmathsetmacro{\lenarray}{0}% 
\foreach \i in #1{%
 \pgfmathtruncatemacro{\lenarray}{\lenarray+1}% 
 \global\let\lenarray\lenarray}%
}  

Example: define array \def\mar{1,2},compute length \getlen{\mar} and get value \lenarray %=> '2'

Note: length of the array is stored in \r with \edef\r{\lenarray}.

Step 2: Expand the single array:

\ifnum\r=1
\def\myArray{{\myArrayRoh,"\relax "}}
\else
\def\myArray{{\myArrayRoh}}
\fi

Step 3: ... so you have to reduce the last item index with 2 instead of 1 for every single array:

\ifnum\r=1
\pgfmathparse{\myArrayLength-2} % single item
\else
\pgfmathparse{\myArrayLength-1} % multiple items
\fi

Result:

enter image description here

MWE:

\documentclass[varwidth,border=7]{standalone}
\usepackage{tikz}

% https://tex.stackexchange.com/a/66159/124842 (@Alain Matthes)
\def\getlen#1{%
\pgfmathsetmacro{\lenarray}{0}% 
\foreach \i in #1{%
\pgfmathtruncatemacro{\lenarray}{\lenarray+1}% 
\global\let\lenarray\lenarray}%
}   
\begin{document}
\def\myArrayRoh{"this"} % singleItemArray

\getlen{\myArrayRoh} 
\edef\r{\lenarray}

\ifnum\r=1
    \def\myArray{{\myArrayRoh,"\relax "}}
\else
    \def\myArray{{\myArrayRoh}}
\fi
%    
\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\ifnum\r=1
\pgfmathparse{\myArrayLength-2}
\else
\pgfmathparse{\myArrayLength-1}
\fi

\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\myArrayRoh{"this","is","cool"} % multiItemArray

\getlen{\myArrayRoh} 
\edef\r{\lenarray}

\ifnum\r=1
    \def\myArray{{\myArrayRoh,"\relax "}}
\else
    \def\myArray{{\myArrayRoh}}
\fi

\pgfmathparse{dim(\myArray)}
\edef\myArrayLength{\pgfmathresult}

\ifnum\r=1
\pgfmathparse{\myArrayLength-2}
\else
\pgfmathparse{\myArrayLength-1}
\fi

\edef\lastIndex{\pgfmathresult}

\foreach \i in {0,...,\lastIndex} 
{
  \noindent Array 2, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult \newline
}
\end{document}
  • Hey @Bobyandbob - very nice, thanks :) – gaurav gupta Oct 23 '17 at 23:09
2

I would suggest to use xstring for a simple string item or listofitems for separated items:

\documentclass{article}
\usepackage{xstring}
\usepackage{pgffor}
\usepackage{listofitems}

\begin{document}


{\bfseries xstring on list:}\vspace{10pt}

\def\myarray{not cool}
\StrLen{\myarray}[\MyStrLen]

Items of "\myarray": \MyStrLen\par
\foreach \i in {1,...,\MyStrLen}{Item \i: ``\StrMid{\myarray}{\i}{\i}''\par}\vspace*{20pt}

{\bfseries listofitems on list:}\vspace{10pt}

\setsepchar{,}
\def\mylist{test item, {another item}, third item}
\readlist\commalist\mylist

Items of "\mylist":\commalistlen\par
\foreachitem\x\in\commalist{Item \xcnt: ``\x''\par}

\end{document}

Output:

enter image description here

Edit after comment:

For one item list, listofitems is still an option.

Adding the follow code on the above file:

\setsepchar{,}
\def\mylist{"not cool"}
\readlist\commalist\mylist

Items of "\mylist":\commalistlen\par
\foreachitem\x\in\commalist{Item \xcnt: ``\x''\par}\vspace{20pt}

We get the desired result:

enter image description here

PS: There is a problem if we enclose the item inside {} that I thing have to be reported to maintainer.

  • Aren't you missing the point here? The idea is to not parse the second one char by char. I think adding answers about a related issue just in case someone find this in a search while looking for something else is a bad idea. – Torbjørn T. Oct 21 '17 at 20:07
  • @TorbjørnT. You are right. But I wasn't really off topic. I just gave a solution on such problems (more for newbies). I edited and added an one item list parsed with listofitems... I still thing this is not the answer that the OP is looking for, but solves such problems (of pursing an one item list-array). I just found an error of the package that I will report to the maintainer so, that the list can be read exactly as the OP is giving it. – koleygr Oct 21 '17 at 21:40
  • 2
    The maintainer of listofitems (@Christian Tellechea) just informed me that he will try to fix the bug as soon as possible. – koleygr Oct 22 '17 at 8:08
1

I'm not sure this is a bug of PGF, as there's no formal definition of what an array is; but it turns out that an array must not have length one:

\documentclass{article}
\usepackage{pgfmath,pgffor}
\begin{document}

\verb|\def\myArray{{"this","is","cool"}}|
\def\myArray{{"this","is","cool"}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 1, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }

\verb|\def\myArray{{"notcool"}}|
\def\myArray{{"notcool"}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 2, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }

\verb|\def\myArray{{12}}|
\def\myArray{{12}}

\pgfmathsetmacro\myArrayLength{dim(\myArray)}

\pgfmathsetmacro\lastIndex{\myArrayLength-1}

\foreach \i in {0,...,\lastIndex} 
 {
  Array 3, item \i: \pgfmathparse{\myArray[\i]}\pgfmathresult\par
 }
\end{document}

enter image description here

The last example gives the same result as with \def\myArray{{1,2}}.

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.