# How to iterate over items in a comma-separated list in ConTeXt?

I have a very simple list like this:

cat, fish, dog, rabbit


(this is is comma-separated, but it is fine if the data is stored in some other way, but it needs to allow for any number of items to be added to the list)

I want to store the list somewhere. Then a simple macro, such as \droptext is placed in the document. The first time it is displayed, it prints "cat", the second time, "fish", etc. The fifth and ninth time it returns to "cat", repeating the loop.

\starttext
\define\mylist{cat, fish, dog, rabbit}

The \droptext\ is a nice animal. So is the \droptext.
Once upon a time, a \droptext\ came to the garden
and ate a \droptext. Everyone was so angry
but then the \droptext came by and brought everyone
a \droptext.

\stoptext


That would print on the page:

The cat is a nice animal. So is the fish.
Once upon a time, a dog came to the garden
and ate a rabbit. Everyone was so angry
but then the cat came by and brought everyone
a fish.

• I tried the \doloop command, as from the description seems like it might do this, but the document couldn't even compile when that command was added.

Is there a way to print items from a list, each time a macro appears in the document, iterating through the items, in ConTeXt?

EDIT: My old answer only works with ConTeXt LMTX. There's an alternative which work with both LMTX and MkIV.

\startluacode

userdata = userdata or {}
userdata.my_lists = {}

interfaces.implement{
name = "registercyclelist",
public = true,
arguments = {"string", "string"},
actions = {
function (name, t)
userdata.my_lists[name] = utilities.parsers.settings_to_array(t)
end
}
}

interfaces.implement{
name = "usecyclelist",
public = true,
arguments = {"string"},
actions = {
function (name)
local first = userdata.my_lists[name][1]
table.remove(userdata.my_lists[name], 1)
table.insert(userdata.my_lists[name], first)
return first
end,
context
}
}

\stopluacode

%Notice the braces
\registercyclelist{firstlist}{cat, fish, dog, rabbit}
\def\droptext{\usecyclelist{firstlist}}

\starttext

The \droptext\ is a nice animal. So is the \droptext.
Once upon a time, a \droptext\ came to the garden
and ate a \droptext. Everyone was so angry
but then the \droptext\ came by and brought everyone
a \droptext.

\stoptext

• When I compile just this without any changes, it prints the error "[ctxlua]:23: attempt to index a nil value (field '?')" – Village Dec 27 '20 at 16:53
• Yeah, I see. I've used a feature not available in LuaTeX. Let me edit my answer – Jairo A. del Rio Dec 27 '20 at 17:00
• @Village See updated answer. – Jairo A. del Rio Dec 27 '20 at 17:06
• I didn't realize there was another version of ConTeXt. I thought I had the latest version, and in fact, updated it last week. – Village Dec 27 '20 at 17:34
• @Village Check your PDF info. If it refers to LuaTeX 1.something (mine is LuaTeX-1.13) it is ConTeXt MkIV (TeX Live is in 1.12, I think); if it refers to LuaTeX 2.something (mine is LuaTeX-2.08) it is ConTeXt LMTX. – Jairo A. del Rio Dec 27 '20 at 17:37

The behavior that you want is exactly how context treats footnote markers etc in the conversion built in conversion sets. See this wiki page but built in conversion set demo. These conversion sets are used to indicate footnotes markers in pages (in some article styles).

So, here is a solution which achieves the desired effect by piggybacking on the counter mechanism:

\defineconversion[animal][cat,fish,dog,rabbit]
\definecounter[myanimals][numberconversion=animal]

\define\droptext{\incrementcounter[myanimals]\convertedcounter[myanimals]}


Basically, define a new conversion called animal, which specifies the conversion set that you want. Then define a counter called myanimal, with the numberconversion equal to animal. Then, droptext simply increments the counter and displays its "converted value". ConTeXt takes care of traversing the list internally.