# Storing an array of strings in a command

I want to store three strings in a variable \mydata like the following:

\storedata\mydata{one}{two}{three}


Then I want to extract the first/second/third string in the following way:

\getdata[1]\mydata   % returns one
\getdata[2]\mydata   % returns two
\getdata[3]\mydata   % returns three


How can I define these commands?

\newcommand\getdata[?] ???

• Can we assume there are always exactly three data items? Do you need the first argument of \getdata to be optional as the LaTeX syntax for [] would suggest? – Joseph Wright Dec 5 '14 at 17:19
• Yes, I am interested in the most simple solution assuming there are exactly three arguments. As for the optional first argument, this is not necessary. – bcp Dec 5 '14 at 17:45
• @bcp: I've added something to this effect - a guaranteed three-argument list - to my answer. – Werner Dec 5 '14 at 18:24

This is classic task for \csname...\endcsname manipulation. You define the control sequence \data:\string\mydata:1 as first parameter, \data:\string\mydata:2 as second parameter etc.

Note that my solution uses another syntax than you suggested for \storedata because we need to know where the list of parameters ends.

\newcount\tmpnum
\def\storedata#1#2{\tmpnum=0 \edef\tmp{\string#1}\storedataA#2\end}
\ifx\end#1\else
\expandafter\def\csname data:\tmp:\the\tmpnum\endcsname{#1}%
\expandafter\storedataA\fi
}
\def\getdata[#1]#2{\csname data:\string#2:#1\endcsname}

\storedata\mydata{{one}{two}{three}}

A:\getdata[1]\mydata   % returns one
B:\getdata[2]\mydata   % returns two
C:\getdata[3]\mydata   % returns three

\bye


Edit: Your self-answer indicates that you need only the macro \storedata with exactly three data-parameters. Then the simple implementation using \ifcase primitive is:

\def\storedata#1#2#3#4{\def#1{\or#2\or#3\or#4}}
\def\getdata[#1]#2{\ifcase\expandafter#1#2\else\outofrange\fi}
\def\outofrange{\errmessage{\string\getdata: out of range 1..3}}

• I understand \storedataA, but could you please explain how \storedata works, particularly how the part \storedataA#2\end processes each {..}? – Jonathan Komar Oct 20 '16 at 10:40

## An implementation with expl3

An implementation with expl3; note that it's easier to use a name instead of a control sequence for the storage bin.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}
{
\bcp_store_data:nn { #1 } { #2 }
}

\DeclareExpandableDocumentCommand{\getdata}{O{1}m}
{
\bcp_get_data:nn { #1 } { #2 }
}

\cs_new_protected:Npn \bcp_store_data:nn #1 #2
{
% create the sequence if it doesn't exist
\seq_if_exist:cF { l_bcp_data_#1_seq } { \seq_new:c { l_bcp_data_#1_seq } }
% populate the sequence
\seq_set_split:cnn { l_bcp_data_#1_seq } { } { #2 }
}
\cs_generate_variant:Nn \seq_set_split:Nnn { c }

\cs_new:Npn \bcp_get_data:nn #1 #2
{
% retrieve the requested item
\seq_item:cn { l_bcp_data_#2_seq } { #1 }
}
\ExplSyntaxOff

\begin{document}

\storedata{mydata}{{one}{two}{three}}

\getdata[1]{mydata}

\getdata[2]{mydata}

\getdata[3]{mydata}

\end{document}


You can even use \getdata[1+1+1]{mydata} or use a counter, say

\newcounter{acounter} % in the preamble

\setcounter{acounter}{2} % somewhere in the document
\getdata[\value{acounter}]{mydata}


The second argument to \storedata should be braced, because otherwise it would be impossible to tell where it ends.

## An extended implementation with expl3

A straightforward extension for also allowing appending to the list, counting the items and removing the last item (optionally storing it in a macro).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}
{
\bcp_store_data:nn { #1 } { #2 }
}

\NewDocumentCommand{\appenddata}{mm}
{
\bcp_append_data:nn { #1 } { #2 }
}

\NewExpandableDocumentCommand{\getdata}{O{1}m}
{
\bcp_get_data:nn { #1 } { #2 }
}

\NewExpandableDocumentCommand{\getlength}{m}
{
\seq_count:c { l_bcp_data_#1_seq }
}

\NewDocumentCommand{\removelast}{om}
{
\IfNoValueTF { #1 }
{
\bcp_remove_last:Nn \l_tmpa_tl { #2 }
}
{
\bcp_remove_last:Nn #1 { #2 }
}
}

\cs_new_protected:Npn \bcp_store_data:nn #1 #2
{
% create the sequence if it doesn't exist or clear it if it exists
\seq_clear_new:c { l_bcp_data_#1_seq }
% append the items
\__bcp_append_data:nn { #1 } { #2 }
}

\cs_new_protected:Npn \bcp_append_data:nn #1 #2
{
% create the sequence if it doesn't exist, do nothing if it exists
\seq_if_exist:cF { l_bcp_data_#1_seq }
{ \seq_new:c { l_bcp_data_#1_seq } }
% append the items
\__bcp_append_data:nn { #1 } { #2 }
}

\cs_new_protected:Npn \__bcp_append_data:nn #1 #2
{
% append items one at a time
\tl_map_inline:nn { #2 }
{
\seq_put_right:cn { l_bcp_data_#1_seq } { ##1 }
}
}

\cs_new:Npn \bcp_get_data:nn #1 #2
{
% retrieve the requested item
\seq_item:cn { l_bcp_data_#2_seq } { #1 }
}

\cs_new_protected:Nn \bcp_remove_last:Nn
{
\seq_pop_right:cN { l_bcp_data_#2_seq } #1
}

\ExplSyntaxOff

\begin{document}

\storedata{mydata}{{one}{two}}

Length is: \getlength{mydata}

\appenddata{mydata}{{three}{four}}

Length is: \getlength{mydata}

\getdata[1]{mydata}

\getdata[2]{mydata}

\getdata[3]{mydata}

\getdata[4]{mydata}

\removelast{mydata}

Length is: \getlength{mydata}

\removelast[\test]{mydata}

\test % should be 'three'

\end{document}


With both solutions, one can also say

\getdata[-1]{mydata}


to retrieve the last item; with -2 the last but one and so on. So

\getdata[-1]{mydata}\par
\getdata[-2]{mydata}\par
\getdata[-3]{mydata}\par
\getdata[-4]{mydata}


would print

four
three
two
one

## A simpler (but less flexible) solution with expl3

Of course there's a much less flexible solution, that I wouldn't recommend, because data in a sequence is better retrievable than in a token list. However, this is the shortest in all presented solutions that have \getdata expandable.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\storedata}{mm}{\tl_set:Nn#1{#2}}
\DeclareExpandableDocumentCommand{\getdata}{O{1}m}{\tl_item:Nn#2{#1}}
\ExplSyntaxOff

\begin{document}

\storedata\mydata{{one}{two}{three}}

\getdata[1]\mydata

\getdata[2]\mydata

\getdata[3]\mydata

\getdata[-1]\mydata

\getdata[-2]\mydata

\getdata[-3]\mydata

\end{document}


## A “classical” implementation

This assumes you just have three items to store.

\documentclass{article}

\newcommand\storedata[4]{\def#1{{#2}{#3}{#4}}}

\makeatletter
\providecommand\@firstofthree[3]{#1}
\providecommand\@secondofthree[3]{#2}
\providecommand\@thirdofthree[3]{#3}

\def\getdata[#1]#2{%
\ifcase#1 \ERROR\or
\expandafter\@firstofthree#2\or
\expandafter\@secondofthree#2\or
\expandafter\@thirdofthree#2\else
\ERROR\fi}
\makeatother

\begin{document}

\storedata\mydata{one}{two}{three}

\getdata[1]\mydata\par   % returns one
\getdata[2]\mydata\par   % returns two
\getdata[3]\mydata\par   % returns three

\end{document}


The \getdata macro is expandable, which isn't in an \ifthenelse based approach.

• Your extended expl3 solution is great! Would it be possible to add a method that removes the last (n:th) item from the list and method that returns/expands to number of items in list? – Jarno_C-137 Sep 27 '17 at 15:37
• @Jarno_C-137 I did a revamp of it. Removing arbitrary items is more complicated. – egreg Sep 27 '17 at 15:50

The following example uses \ltx@CarNumth from package ltxcmds to select an element from a group token list. The list can be stored as simple macro:

\documentclass{article}

\usepackage{ltxcmds}
\makeatletter
\newcommand*{\getdata}[2]{%
\expandafter\ltx@CarNumth\expandafter{%
\the\numexpr(#1)\expandafter
}#2\@nil
}
\makeatother

\newcommand*{\mydata}{{one}{two}{three}}

\newcommand*{\mylongdata}{
{one} {two} {three} {four} {five} {six} {seven} {eight} {nine} {ten}
{eleven} {twelve} {thirteen} {fourteen} {fifteen} {sixteen}
{seventeen} {eighteen} {nineteen} {twenty} {twenty-one} {twenty-two}
{twenty-three} {twenty-four}
}

\begin{document}
\begin{itemize}
\item \getdata{1}\mydata
\item \getdata{2}\mydata
\item \getdata{3}\mydata
\item \getdata{24}\mylongdata
\end{itemize}
\end{document}


Remarks:

• The solution (\getdata) is fully expandable.
• The numeric argument can also contain expressions like 1+2. (Internally e-TeX's \numexpr is used for the expressions.)

Here is a LuaLaTeX solution. It is preferable to always keep your data, separately from your code. Also the \data command is preferable to be a name. In the example below is a filename.

\documentclass{article}
\usepackage{luatextra, filecontents}
\begin{filecontents*}{numbers.lua}
local m = m or {}
m = {
"one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven",
"twelve", "thirteen"
}
return m
\end{filecontents*}
\begin{document}
\def\getfields[#1]#2{%
local numbers = require('#2')
local s = string.split('#1', ',')
for k,v in pairs(s) do
tex.print(numbers[tonumber(s[k])]..', ')
end
}}

\getfields[1,3,5,8]{numbers}
\end{document}


Why I prefer a LuaTeX solution, is that the LuaTeX code is now maturing and offers unlimited opportunities. In the example above, you can structure your data file in any way you want.

• No problem with the idea of using Lua, but I'd caution athat your first argumetn here is not optional so doesn't follow the usual LaTeX convention. – Joseph Wright Dec 5 '14 at 17:14
• @JosephWright Thanks for the comment. The first argument was an ugly hack. From a UI point of view provided the data always resides in the same file one needs only \getfields{1,2,3,4}. The beauty of Lua it could even be \getfields{1,...,n}. – Yiannis Lazarides Dec 6 '14 at 9:43

This solution extends the answer of wipet:

• Error messages are added, if the selector number is out of range or the data are unknown.

• The number of items are stored at position 0.

• \getdata also accepts negative arguments, which count from the end of the list.

• As in wipet's solution, the number of items is only limited to TeX's maximum integer value (231 - 1 = 2147483647). This limit could be made unlimited by use of package bigintcalc, but in practice memory limits like the hash table size will be very likely hit before.

• The numeric argument of \getdata also accepts expressions, because the argument is passed through e-TeX's \numexpr.

• The solution (\getdata) is expandable except for the error cases. Messages like error messages are not expandable.

• Just the name without the backslash is used instead of the command sequence.

The full example:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[variablett]{lmodern}

\makeatletter
\newcommand*{\storedata}[2]{%
\count@=0 %
\@tfor\@tmp:=#2\do{%
\expandafter\let\csname data:\the\count@:#1\endcsname\@tmp
}%
\expandafter\edef\csname data:0:#1\endcsname{\the\count@}%
}
\newcommand*{\getdata}[2]{%
\@ifundefined{data:0:#2}{%
\@latex@error{Undefined data #2'}\@ehc
}{%
\expandafter\@getdata\expandafter{%
\the\numexpr
\ifnum\numexpr(#1)<\z@
\@nameuse{data:0:#2}+1+%
\fi
(#1)%
\relax
}{#2}{#1}%
}%
}
\newcommand*{\@getdata}[3]{%
\ifnum#1<\z@
\@getdata@error{\the\numexpr(#3)\relax}{#2}%
\else
\ifnum#1>\@nameuse{data:0:#2} %
\@getdata@error{#1}{#2}%
\else
\@nameuse{data:#1:#2}%
\fi
\fi
}
\newcommand*{\@getdata@error}[2]{%
\@latex@error{%
Wrong data selector #1 for #2',\MessageBreak
which only contains \@nameuse{data:0:#2} item(s)%
}\@ehc
}
\makeatother

\storedata{mydata}{{one}{two}{three}}
\storedata{mylongdata}{
{one} {two} {three} {four} {five} {six} {seven} {eight} {nine} {ten}
{eleven} {twelve} {thirteen} {fourteen} {fifteen} {sixteen}
{seventeen} {eighteen} {nineteen} {twenty} {twenty-one} {twenty-two}
{twenty-three} {twenty-four}
}

\begin{document}
\newcommand*{\test}[2]{%
\ttfamily #2[#1]: &
\getdata{#1}{#2}
\tabularnewline
}
\begin{tabular}{@{}l@{ }l@{}}
\test{0}{mydata}
\test{1}{mydata}
\test{2}{mydata}
\test{3}{mydata}
\test{-1}{mydata}
\test{-2}{mydata}
\test{-3}{mydata}
\test{-4}{mydata}
\hline
\test{0}{mylongdata}
\test{1}{mylongdata}
\test{24}{mylongdata}
\test{-10}{mylongdata}
\end{tabular}
\end{document}


arrayjobx is meant to fulfil this need:

\documentclass{article}
\usepackage{arrayjobx}
\begin{document}

\newarray\mydata

\verb|\mydata(1)|: \mydata(1) \par
\verb|\mydata(2)|: \mydata(2) \par
\verb|\mydata(3)|: \mydata(3)
\end{document}


If you will always be using only three items and require the interface you mentioned, then the following will do:

\documentclass{article}
\makeatletter
% http://tex.stackexchange.com/a/42337/5764
\begingroup\lccode\|=\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\macroname}[1]{\expandafter\removebs\string#1}

\newcommand{\storedata}[4]{%
\@namedef{\macroname{#1}@1}{#2}% Store first item
\@namedef{\macroname{#1}@2}{#3}% Store second item
\@namedef{\macroname{#1}@3}{#4}}% Store third item
\newcommand{\getdata}[2]{\@nameuse{\macroname{#2}@#1}}

\def\getdata[#1]#2{\@nameuse{\macroname{#2}@#1}}
\makeatother

\begin{document}

\storedata{\mydata}{one}{two}{three}

\verb|\getdata[1]\mydata|: \getdata[1]\mydata   % returns one

\verb|\getdata[2]\mydata|: \getdata[2]\mydata   % returns two

\verb|\getdata[3]\mydata|: \getdata[3]\mydata   % returns three

\end{document}


If a more traditional interface like \getdata{2}\mydata is required, then you can use

\newcommand{\getdata}[2]{\@nameuse{\macroname{#2}@#1}}

• Differently from wipet's and my answer, \mydata(1) is not fully expandable; to the contrary, our \getdata macros are fully expandable. – egreg Dec 5 '14 at 16:16

This makes a very minor adjustment to your own preferred solution, so that you will get some information if you pass, say, \getdata[4].

\documentclass{article}
\usepackage{ifthen}
\newcommand\selectnth[4]{%
\ifthenelse{\equal{#4}1}{#1}{%
\ifthenelse{\equal{#4}2}{#2}{%
\ifthenelse{\equal{#4}3}{#3}{%
}%
}%
}%
}

\newcommand\storedata[4]{%
\newcommand#1{%
\selectnth{#2}{#3}{#4}}}

\newcommand\getdata[2][1]{#2{#1}}
\begin{document}
\storedata\mydata{one}{two}{three}
\getdata[1]\mydata   % returns one
\getdata[2]\mydata   % returns two
\getdata[3]\mydata   % returns three
\getdata[4]\mydata   % returns warning
\end{document}


All this does is nest your \ifthenelse conditionals, and add a \typeout message if all conditionals return false. This does not add much complexity, but it may save you from hours searching for the source of a mysterious error later (when you've forgotten what you put in your code - at least, if you are at all like me).

REVISED SOLUTION (listofitems package)

What you are asking is exactly what the listofitems package excels at, and much more.

\documentclass{article}
\usepackage{listofitems}
\begin{document}
% DELIMITER CAN BE CHANGED WITH \setsepchar{}, DEFAULT COMMA
% NESTED ITEM LISTS CAN ALSO BE DONE
% * OPTION REMOVES LEADING/TRAILING SPACES
\mydata[1]

\mydata[2]

\mydata[3]

The list contains \mydatalen{} items.
\end{document}


ORIGINAL SOLUTION (readarray package)

If you are willing to wrap the data in a group, and space-separate the items, then the \getargsC macro of readarray can do it directily.

\documentclass{article}
\begin{document}
\getargsC{{one} {phrase two} {three}}
\narg\ items\par
item2 is \argii, while items 1 and 3 are \argi\ and \argiii.
\end{document}


• This is an excellent solution which can be enhanced with the multido package. Replace all of your \mydata[n] lines with the single line: \multido{\n=1+1}{\mydatalen{}}{\mydata[\n]\\} for a programmatic solution to the problem. – WesH Sep 16 '20 at 18:07
• @WesH Does not need multido. Use listofitems native commands: \foreachitem\n\in\mydata[]{\mydata[\ncnt]\\}. Or more simply, still: \foreachitem\n\in\mydata[]{\n\\} – Steven B. Segletes Sep 16 '20 at 18:12
• Thanks, I did not know that was an option for listofitems. I guess I need to RTFM! – WesH Sep 16 '20 at 18:14
• @WesH Definitely RTFM. Nested parsing is very powerful. See example at tex.stackexchange.com/questions/328432/… – Steven B. Segletes Sep 16 '20 at 18:18

If you can accept comma (or almost anything except {}) delimited strings, one can use the xstring package. I added a second argument to \getdata in case you wanted to have more than one array.

\documentclass{article}
\usepackage{xstring}

\newcommand{\mydata}{one,two,three}

\newcounter{comma}
\newcommand{\tempstr}{}% reserve name
\newcommand{\getdata}[2][1]% #1 = index, #2 = array name
{\ifnum#1=1 \StrBefore{#2}{,}[\tempstr]%
\StrCount{#2}{,}[\tempstr]%
\ifnum\value{comma}=\tempstr\StrBehind[\thecomma]{#2}{,}[\tempstr]%
\else\StrBetween[\thecomma,#1]{#2}{,}{,}[\tempstr]%
\fi\fi\tempstr}

\begin{document}
A:\getdata[1]{\mydata}

B:\getdata[2]{\mydata}

C:\getdata[3]{\mydata}
\end{document}


Here is a solution using pgffor :

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

% the get macro using foreach
\def\get(#1)#2{
\foreach[count=\i] \element in #2 {
\ifnum \i = #1 \element\fi
}
}

\begin{document}
% store the data
\newcommand{\data}{one,two,{three, with comma}}

% use the data
\begin{enumerate}
\item \get(1)\data
\item \get(2)\data
\item \get(3)\data
\end{enumerate}
\end{document}


EDIT: Here is another solution using pgfmath parser who defines access to arrays. The following code produce the same result.

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

% the get macro using pgfmathparse
\def\get#1{\pgfmathparse{#1}\pgfmathresult}

\begin{document}
% store the data
\def\data{{"one","two","three, with comma"}}

% use the data
\begin{enumerate}
\item \get{\data[0]}
\item \get{\data[1]}
\item \get{\data[2]}
\end{enumerate}
\end{document}


Thank you for the answers. Guess I was looking for the following simple code:

\newcommand\selectnth[4]{%
\ifthenelse{\equal{#4}1}{#1}{}%
\ifthenelse{\equal{#4}2}{#2}{}%
\ifthenelse{\equal{#4}3}{#3}{}%
}

\newcommand\storedata[4]{%
\newcommand#1{%
\selectnth{#2}{#3}{#4}}}

\newcommand\getdata[2][1]{#2{#1}}

• What would you like \getdata[4] to do? With this implementation it fails silently. – Matthew Leingang Dec 5 '14 at 20:08
• Here I assumed that there are exactly three arguments. Indeed, \getdata[4] returns nothing. – bcp Dec 5 '14 at 20:12