How to display content from an array vertically in a table?

Hi,
I do not manage to display vertically in a table data that are stored in an array.
I have written some code to illustrate it.
Solution 2 works as it is hard coded, but solution 3 does not as the spray is not seen inside the align(center). How can I expose …questions_2 inside the align() ?
Thanks in advance for your answer.

#let questions_2 = (  
  [2 x 1 =],
  [2 x 2 =],
  [2 x 3 =],
  [2 x 4 =],
  [2 x 5 =]  
)

#let questions_3 = (  
  [3 x 1 =],
  [3 x 2 =],
  [3 x 3 =],
  [3 x 4 =],
  [3 x 5 =]  
)

Solution 1 horizontal
#grid(
    columns: (1fr, 1fr, 1fr),
    gutter: 10pt,

    ..questions_2.map(questionDisplay => [My question: #questionDisplay])
)


\
\



Solution 2 : harcoded works
#grid(
    columns: (1fr, 1fr, 1fr),
    gutter: 10pt,

    align(center)[
      my question questions.at(0) 
      [My question: 3 x 7 = .........]
      [My question: 3 x 2 = .........] 
      [My question: 3 x 8 = .........] 
      [My question: 3 x 9 = .........] 
      [My question: 3 x 3 = .........] 
      [My question: 3 x 6 = .........] 
      [My question: 3 x 4 = .........]     
    ],
    align(center)[
      [My question: 5 x 5 = .........] 
      [My question: 5 x 7 = .........] 
      [My question: 5 x 2 = .........] 
      [My question: 5 x 8 = .........] 
      [My question: 5 x 9 = .........] 
      [My question: 5 x 3 = .........] 
      [My question: 5 x 6 = .........] 
      [My question: 5 x 4 = .........]     
    ],
    align(center)[
      [My question:  4 x 5 = .........] 
      [My question:  4 x 7 = .........] 
      [My question:  4 x 2 = .........] 
      [My question:  4 x 8 = .........] 
      [My question:  4 x 9 = .........] 
      [My question:  4 x 3 = .........] 
      [My question:  4 x 6 = .........] 
      [My question:  4 x 4 = .........]     
    ]
  ),
)


\
\


Solution 3
#grid(
    columns: (1fr, 1fr, 1fr),
    gutter: 10pt,

    align(center)[
      ..questions_2,
    ],
    align(center)[
      ..questions_3
    ]
)

The layout order of grid is row-major, i.e, for a sequence 0..5 you’d get:

#grid(columns: 3
  [0], [1], [2],
  [3], [4], [5],
)

As I understand it you’d like to display it in a column-major fashion for the same sequence:

#grid(columns: 3
  [0], [2], [4],
  [1], [3], [5],
)

To do this you need to transpose the sequence with respect to the number of columns, you can transpose an array by using zip followed by flatten:

#let questions2 = (  
  [2 x 1 =],
  [2 x 2 =],
  [2 x 3 =],
  [2 x 4 =],
  [2 x 5 =],
)

#let questions3 = (  
  [3 x 1 =],
  [3 x 2 =],
  [3 x 3 =],
  [3 x 4 =],
  [3 x 5 =],
)

#let questions4 = (  
  [4 x 1 =],
  [4 x 2 =],
  [4 x 3 =],
  [4 x 4 =],
  [4 x 5 =],
)

#let questions = array.zip(questions2, questions3, questions4).flatten()

#set align(center)
#grid(columns: (1fr, 1fr, 1fr), inset: 1em, ..questions)

You can see that we have exactly as many arrays as we have columns, we then zip them up such that we get a sequence of triplets, one from each sequence and then we concatenate these triplets back into one larger sequence, effectively transposing the array.

Resulting in the following output:

As an aside, your definitions can be automated too:

Summary
#let questions(n, k) = range(1, k + 1).map(i => [#n x #i =])
#let questions = array.zip(..range(2, 4).map(n => questions(n, 5))).flatten()

#set align(center)
#grid(columns: (1fr, 1fr, 1fr), inset: 1em, ..questions)
3 Likes
#let questions2 = range(5).map(i => [2 x #(i + 1) =])
#let questions3 = range(5).map(i => [3 x #(i + 1) =])

#let formatter = it => [My question: #it]

#let column1 = questions2.map(formatter)
#let column2 = questions3.map(formatter)
#let column3 = ([],) * column1.len()

#grid(
  columns: (1fr, 1fr, 1fr),
  gutter: 10pt,
  ..questions2.map(it => (it, [], [])).flatten()
)

#v(8em)

#grid(
  columns: (1fr,) * 3,
  gutter: 10pt,
  ..array.zip(column1, column2, column3).flatten()
)

image

To make it a little bit more flexible:

#let columns = (column1, column2, column3)
#grid(
  columns: (1fr,) * columns.len(),
  gutter: 10pt,
  ..array.zip(..columns).flatten()
)

To build on @Tinger’s answer, a reusable function for transposing a 1D array could look like this:

#let arr = ([a], [b], [c], [d], [e])

/// Transpose a 1D-array as if it was a 2D array. The array is treated as if it
/// was width*height (width is inferred) and ends up as height*width. The array
/// is padded with `none` elements to handle non-"rectangular" arrays.
#let transpose(arr, height) = {
  let width = calc.ceil(arr.len() / height)
  let missing = width * height - arr.len()
  // add dummy elements so that the array is "rectangular"
  arr += (none,) * missing
  // transpose the array
  array.zip(..arr.chunks(width)).join()
}

// treat the array as having 3 columns
#table(columns: 3, ..arr)

// treat the array as having 3 rows,
// transposing it to have 3 columns instead
#table(columns: 3, ..transpose(arr, 3))
Result

transpose

In your case, you could call this as

transpose((..questions2, ..questions3, ..questions4), 3)
2 Likes

Amazing! Really clear and well documented solutions! And quick too! Thanks a lot for your precise answers, I can now close the topic as I have my answer.
It will help me to quickly build evaluations for my class!

1 Like

You’re welcome! If a post answered your question, can you please mark it as a solution? This will let newcomers know that this question is already answered, which can speed up “answer searching”. :)

One little comment for the readers who would find this solution. It works for my needs but you will need exactly the same numbers of questions in each array zipped otherwise a full result line is missing. This is the way zip works if I understood it well.

Correct, from array.zip’s documentation:

If the arrays to be zipped have different lengths, they are zipped up to the last element of the shortest array and all remaining elements are ignored.

You probably can make some intermediate logic that will fill the shortest arrays with additional empty elements (or add the missing elements manually) so that they all have the same length and nothing will be ignored when zipping them up.

just so it doesn’t go under: my response’s transpose function does handle arrays of “odd” lengths.

(I just realized that I may have screwed up in the exact handling, but I’ll fix it after confirming – edit: it’s fixed)

PS: you ticked Andrew’s response; if you look at your original post in this thread, you’ll see that it appears there up there. Please tick one of the responses that contains the actual answer so that that is the one that appears up there - be that Andrew’s other response, or a different one.

Yes, I get the principle now (never hear about zip before!), I had to do it with paper and pen to understand but this is easy. Thanks!

1 Like

Tick problem solved :)

1 Like

I need to go from n arrray to n array as I need to have table 3 table 4 and table 5 in the right column, so I am not sure that a solution using a 1 array input could work.

That sounds like a bit of a different issue, not directly about having column-ordering of cells in a table. could you ask this in a new thread? With a bit more detail; as it is I’m not sure what “table 3 table 4 and table 5 in the right column” means – what’s with the other columns? (Address this in your new post, not in this thread).

Also, do keep in mind that you can ask as many questions as you want, but to not clutter one topic with everything, they all should be creating separately (via “+ New Topic” button at https://forum.typst.app/). It’s very tempting to ask follow-up questions (in the same topic), I know from experience. We must resist the urge! /j

I did not want to ask any question, just commented that the last solution was not the right one for me :slight_smile:
Thanks a lot for the great help!