# Selecting random elements from a comma separated list

I would like to have a command (either user-defined, or from a package, or whatever) that has the basic form:

\selectNrandom{N}{list, of, comma, separated, elements}{code to execute}


which will select N distinct, random elements from the csv list that follows, and then executes the code on those elements.

For example, this would select two elements and then typeset them with a large space between them (think math quiz):

\selectNrandom{2}{N, W, Z, Q, R, C}{%
}


I don't know what kind of MWE to post other than the above since I don't really have any idea of how to even begin writing a macro to do this.

Justification for Asking (i.e., I did do my homework!): I have been reading through sources such as various package documentations (etextools, probsoln, datatool, etc) as well as a few books (Joy of TeX, The Advanced TeXbook) and various websites. But I am still very new to the whole programming aspects of LaTex and Tex.

-

Here's a version with xparse and LaTeX3 code, with the help of the random.tex file by D. Arsenau

\documentclass{article}
\usepackage{xparse}
\input{random}

\ExplSyntaxOn
\NewDocumentCommand{\htguse}{ m }
{
\use:c { htg_arg_#1: }
}
\NewDocumentCommand{\selectNrandom}{ m m m }
{
\htg_select_n_random:nnn { #1 } { #2 } { #3 }
}

\cs_new_protected:Npn \htg_select_n_random:nnn #1 #2 #3
{
\seq_clear:N \l_htg_used_seq
\int_set:Nn \l_htg_length_int { \clist_count:n { #2 } }
\int_compare:nTF { #1 > \l_htg_length_int }
{
\msg_error:nnxx { randomchoice } { too-many } { #1 } { \int_to_arabic:n { \l_htg_length_int } }
}
{
\int_step_inline:nnnn { 1 } { 1 } { #1 }
{
\htg_get_random:
\cs_set:cpx { htg_arg_##1: }
{ \clist_item:nn { #2 } { \l_htg_random_int } }
}
#3
}
}
\cs_new_protected:Npn \htg_get_random:
{
\setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }
\seq_if_in:NxTF \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } }
{ \htg_get_random: }
{ \seq_put_right:Nx \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } } }
}
\seq_new:N \l_htg_used_seq
\int_new:N \l_htg_length_int
\int_new:N \l_htg_random_int
\msg_new:nnnn { randomchoice } { too-many }
{ Too~ many~choices }
{ You~want~to~select~#1~elements,~but~you~have~only~#2 }
\ExplSyntaxOff

\begin{document}

\selectNrandom{2}
{N, W, Z, Q, R, C}
{$\mathbf{\htguse{1}}$ and $\mathbf{\htguse{2}}$}

\selectNrandom{3}
{A, B, C}
{$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\selectNrandom{3}
{N, W}
{$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\end{document}


The macros take care to check that distinct elements are chosen by maintaining the list of already extracted elements and doing a new choice if a number is extracted again.

You refer to the first, second, and so on, element by \htguse{1}, \htguse{2} and so on.

The third call will raise an error:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! randomchoice error: "too-many"
!
! Too many choices
!
! See the randomchoice documentation for further information.
!
! For immediate help type H <return>.
!...............................................

l.56 ...bf{\htguse{2}}$and$\mathbf{\htguse{3}}\$}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| You want to select 3 elements, but you have only 2
|...............................................


-
Wouldn't it be better to make a copy of the list and delete each selected element from the list? It might take longer for long lists and small n's, but I guess it could be much faster in the opposite situation. (Much depends on the actual data structure, I guess, and me not being a computer scientist means I might be mistaken.) –  mbork Dec 8 '12 at 23:21
@mbork I think that removing an element from a list is slower than recomputing a random number, but I might be wrong. If it ain't broken, don't fix it. :) And the current macros of LaTeX3 don't allow for removing a specified element from a clist, so I'd have to implement it. –  egreg Dec 8 '12 at 23:25
Well, I'm not sure, but recomputing a random number a few times... Anyway, I'd guess that for quite a few applications, the list would be much longer than n anyway. –  mbork Dec 9 '12 at 0:48
@egreg I just copy/pasted your code into a new document and ran it. I'm getting an Undefined control sequence error. It is complaining about \clist_count:n. –  HTG Dec 9 '12 at 3:13
@HTG Yes, the code needs the last version of l3kernel and relative packages. Sorry if you had some inconveniences; but updating regularly is a good thing to do. –  egreg Dec 9 '12 at 21:02

Here's a Lua solution, the chosen arguments can be accessed as #1, #2, etc. As I haven't fully wrapped my head around the interaction between TeX and lua with respect to expansion, I can't say that I'm sure that using #1 etc. will behave in the same way as you might expect in a regular macro definition.

\documentclass{article}
\usepackage{luacode}

\begin{luacode*}
local rand = math.random
local args = {}
function getnrand(n,l,f)
tab = string.explode(l,",")
for i = 1,n do
local num = rand(#tab)
args[i] = tab[num]
table.remove(tab,num)
end
f = string.gsub(f,"#(%d+)",
function(n)
end)
tex.sprint(f)
end
\end{luacode*}

\long\def\selectNrandom#1#2#3{\directlua{getnrand(#1,"\luatexluaescapestring{\unexpanded{#2}}","\luatexluaescapestring{\unexpanded{#3}}")}}

\begin{document}

\selectNrandom{3}{a,b,c,d,e,f,g}{
The first random letter is: \textbf{#1}

The second random letter is: \textbf{#2}

The third random letter is: \textbf{#3}
}

\end{document}

-
Does it work also with #10 and so on? I guess so, and it would be quite a practical way to use the results. –  egreg Dec 8 '12 at 23:56
@egreg Yes it will work with #n with n>9. The gsub part finds integers following a # and then replaces the #blah string with the appropriate argument. Since it wasn't too hard to maintain the # syntax, I also thought it would be sort of handy. –  Scott H. Dec 9 '12 at 0:21
@ScottH. Thanks for your answer and other help. I selected egreg's answer only because I didn't have to change how I compiled my files, whereas using LuaTeX would be a bigger change for me. –  HTG Dec 12 '12 at 22:15
No problem, I knew that @egreg would beat me to an xparse version, so I went with Lua. I'm glad that you got your question answered, and I learned something making the solution so everybody comes out ahead :) –  Scott H. Dec 12 '12 at 22:30