'Randomize items in rmarkdown transparently
I have the following rmarkdown document:
---
title: "Untitled"
output: pdf_document
date: '2022-04-28'
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
1. This is the first outer item.
(a) A
(b) B
2. This is the second outer item.
(a) C
(b) D
3. This is the third outer item.
(a) E
(b) F
I would like to randomly shuffle this list simultaneously in two ways: Inside the inner lists and outside those. Say, I want to get something like the following:
1. This is the second outer item.
(a) D
(b) A
2. This is the first outer item.
(a) B
(b) A
3. This is the third outer item.
(a) F
(b) E
I know that I could accomplish that by creating the rmarkdown code by programming with strings. So, my question is: Can one achieve the goal by writing the text of the document without having to resort to programming with strings? The reason is that I would like to have a clear picture of the document as I write it, without having the text of the document buried in code. (I am aware that some programming is unavoidable though.)
Solution 1:[1]
I'd do it as follows: Write your text just as you did above, and have a function convert that into a structure like a list of lists. Then randomize the list of lists, and convert back to text. Don't forget to save the answer key! Here's an example for the first conversion:
questions <- "
1. This is the first outer item.
(a) A
(b) B
2. This is the second outer item.
(a) C
(b) D
3. This is the third outer item.
(a) E
(b) F
"
toListOfLists <- function(text) {
text <- unlist(strsplit(text, "\n"))
# These regular expressions match the first line of a question
# or an answer. \\1 will be the text.
qreg <- "^[[:digit:]]+[.](.*)"
areg <- "^[[:space:]]*[(][[:alpha:]][)](.*)"
qstart <- grep(qreg, text)
astart <- grep(areg, text)
class <- rep("", length(text))
class[qstart] <- "q"
class[astart] <- "a"
result <- list()
for (i in seq_along(qstart)) {
line <- qstart[i]
question <- sub(qreg, "\\1", text[line])
line <- line + 1
while (line < length(text) & class[line] == "") {
question <- paste(question, text[line], sep="\n")
line <- line + 1
}
this_astart <- astart[astart >= line]
if (line < max(qstart))
this_astart <- this_astart[this_astart < min(qstart[qstart > line])]
answers <- list()
for (j in seq_along(this_astart)) {
line <- this_astart[j]
answers[[j]] <- sub(areg, "\\1", text[line])
line <- line + 1
while (line < length(text) & class[line] == "")
answers[[j]] <- paste(answers[[j]], text[line], sep="\n")
}
result[[i]] <- list(question = question, answers = answers)
}
result
}
toListOfLists(questions)
#> [[1]]
#> [[1]]$question
#> [1] " This is the first outer item."
#>
#> [[1]]$answers
#> [[1]]$answers[[1]]
#> [1] " A"
#>
#> [[1]]$answers[[2]]
#> [1] " B"
#>
#>
#>
#> [[2]]
#> [[2]]$question
#> [1] " This is the second outer item."
#>
#> [[2]]$answers
#> [[2]]$answers[[1]]
#> [1] " C"
#>
#> [[2]]$answers[[2]]
#> [1] " D"
#>
#>
#>
#> [[3]]
#> [[3]]$question
#> [1] " This is the third outer item."
#>
#> [[3]]$answers
#> [[3]]$answers[[1]]
#> [1] " E"
#>
#> [[3]]$answers[[2]]
#> [1] " F"
Created on 2022-04-29 by the reprex package (v2.0.1)
Something like your existing solution for the randomization and conversion back to text will work on this structure.
Solution 2:[2]
Part of the solution is to use latex. An example where the answers are randomized is shown below. But I am not able to randomize the Questions as well. Maybe a deeper look into the manual can bring up a solution.
---
title: "List Randomize"
header-includes:
- \usepackage{randomlist}
output: pdf_document
---
## Test randomize
Eine Liste, randomisiert:
\begin{enumerate}
\item Question 1
\RandomEnumerateList{
answer1}{
answer2}{
answer3}
\item Question 2
\RandomItemizeList{
answer1}{
answer2}{
answer3}
\end{enumerate}
Package randomlist: https://ctan.joethei.xyz/macros/generic/randomlist/randomlist.pdf
Solution 3:[3]
This is the best that I have achieved until now:
---
title: "Untitled"
output: pdf_document
date: '2022-04-28'
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(tidyverse)
```
```{r include=FALSE}
questions <- list(
question1 = list(
enun = "This is the first outer item.",
ans1 = "A.",
ans2 = "B.",
ans3 = "X.",
ans4 = "Y."
),
question2 = list(
enun = "This is the second outer item.",
ans1 = "C.",
ans2 = "D."
),
question3 = list(
enun = "This is the thrid outer item.",
ans1 = "E.",
ans2 = "F."
)
)
```
```{r echo=FALSE, results='asis'}
qexpand <- function(x)
{
s1 <- str_c("1. ", x[[1]])
s2 <- map_chr(sample(2:length(x)), ~ str_c( "\n (a) ", x[[.x]])) %>%
str_c(collapse = " ")
str_c(s1, s2)
}
scr <- map(questions[c(3,1,2)], qexpand)
res = knitr::knit_child(text = scr, quiet = TRUE, )
cat(res, sep="\n")
```
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | user2554330 |
| Solution 2 | Lucas |
| Solution 3 | PaulS |

