Quick Links: Gideros Home | Download Gideros | Developer Guide
Use # for a multi-dimensional table?
  • This is a pretty simple question, but I just don't know the answer. I’m not sure how to use “#” in a for loop.

    I have a 2 dimensional array, questions(a)(b). The first index (a) indicates the number of the question. The first element of the second index (b) is the text of the question (which is a multiple choice question) and the following elements are the possible answers.

    For example (This is for the sixth question in the table.)
    questions[6] [question] = “What year was the Declaration of Independence signed?”
    questions[6] [correctAnswer] = “1776”
    questions[6] [wrongAnswer1] = “1917”
    questions[6] [wrongAnswer2] = “1812”
    questions[6] [wrongAnswer3] = “1865”
    questions[6] [wrongAnswer4] = “1945”

    I want to run these questions through a for loop based solely on the first index. That is, I want the loop to do something to the first question, then the same thing to the second and so on. If it was a one dimensional array, I would just do for i=1,#questions, with #questions returning the number of questions. However, I suspect #questions counts all of the elements in the table. If each question has 1 question and 4 answers (i.e, the second index would have possibilities 1-5) #questions would return a number that is 5 times the number of questions in the table. Is this correct?

    If I knew for sure that every question had 5 elements as in the example above, I could just use #questions/5 instead of #questions. But some questions only have 3 choices, not 4, so there’s no way I could do that.

    Is there a way to get the number of elements in each of the dimensions of the table?
    For example (a is the number of elements in first dimension and b is the number of elements in the second dimension):
    for i=1,a do
    for j=1,b do
    code to be performed
    end
    end

    That is, what can I use in my code in place of a and b to loop through the number of items in each dimension of the table?
  • SinisterSoftSinisterSoft +1 -1 (+1 / -0 )
    Maintainer
    assuming you have questions in a table from 1 to n, you can use # to find the number of entries in the table. Then assuming each question has a sub-table that doesn't use numbers to index it then you can use in pairs. Here is an example...
    for loop=1,#questions do
    local question=questions[loop]
    for key,value in pairs(question) do
    print(key,value)
    end
    end


    Likes: antix

  • rpallen +1 -1 (+1 / -0 )
    Member
    Thanks, SinisterSoft.

    Likes: SinisterSoft

  • pie +1 -1
    Member
    @rpallen just a couple of notes from my personal experience to integrate SinisterSoft's answer:

    pairs is wonderful, but "slow". maybe you won't notice that in this app because you run it only once in a while. However watch out for performance if you need to "abuse" of it :) also note that pairs outcome may vary its order (somewhere someone made a pairsByKeys lua function to avoid this, but it's even slower because it has to check and order elements before returning them).
    Usually nested for loops are performance killers, but sometimes you can't avoid this.

    However the fastest way to operate on tables with for loops is using numerical indices.
    for loop=1,#questions do

    The lenght operator # works only on strings (ie. to know how many chars are there) and on tables with numerical indices.
    You may experience strange behaviours -like 0 counts- if your table has string indices (or mixed string and number indices).

    I think that I personally would write my questions and answers with numerical indices, in a way that the question and the correct answer are always in the same place (index), and my app knows that.
    ie:

    questions[6] = {
    “What year was the Declaration of Independence signed?”,
    “1776”,
    “1917”,
    “1812”,
    “1865”,
    “1945”
    }

    [1] the question
    [2] the correct answer
    how many answers are there = #questions[6] - 1
    (where the 1 we are taking away is the first table entry, because it is the question in this scenario, but it could also have been the last..., so that questions[6][ #questions[6] ] was the question)

    or

    questions[6] = {
    q = “What year was the Declaration of Independence signed?”,
    a = {
    “1776”,
    “1917”,
    “1812”,
    “1865”,
    “1945”
    }
    }

    [q] the question
    [a] the answers, [a][1] is the correct one.
    how many answers are there = #questions[6][a]

    Both ways would enable to check how many answers are there with the lenght operator, then I would mix the answers on a random basis when placing them on screen (but here I suppose I would still need for and maybe pairs. Just once though, when I need to place the answers on screen) .

    Another option would be to add a "correct answer" value to each questions entry:
    questions[6]["c"] = 1 (assuming that the right answer is on index 1) to tell the app which answer is correct (which index inside "a" contains the right answer), in this way you won't need to mix the answers, because you can write them already in the order you like. But everytime you play, the answers would be in the same position.

    edit: i forgot to answer this

    If each question has 1 question and 4 answers (i.e, the second index would have possibilities 1-5) #questions would return a number that is 5 times the number of questions in the table. Is this correct?


    no, assuming you have questions in a table from 1 to n (using numerical indices), # would return n .
    If you have one (or more) sub-tables their value would always count 1 each (# just counts each entry at the "table level" you ask. see my examples before)
  • Thanks, pie.

    I do use two randomizing functions, one to randomize the questions and another to randomize the answers. I need 2 functions because they need to operate a bit differently. The one for the answers has to both randomize the order of the answers and keep track of the position of the correct answer. The randomizer for the questions only needs to randomize the order of the questions.
  • pie +1 -1
    Member
    @rpallen I thought that you could even build your question as:

    questions[6] = {
    q = “What year was the Declaration of Independence signed?”,
    a = {
    {“1776”},
    “1917”,
    “1812”,
    “1865”,
    “1945”
    }
    }

    in this way you could use the same function for both "randomize" operations, without the need to keep track of the correct answer position.
    You may get the correct answer just looking at the data type:
    local output 
    for n=1, #questions[6][a] do
    if type(questions[6][a][n]) == 'table' then
    --this is the correct answer
    output = questions[6][a][n][1]
    else
    --this is a wrong answer
    output = questions[6][a][n]
    end
    end
  • antixantix +1 -1
    Member
    What about just keeping track of which question is correct?
    questions = {
    {
    question = “What year was the Declaration of Independence signed?”,
    answers = {
    {text =1776”, correct = true},
    {text =1917”, correct = false},
    {text =1812”, correct = false},
    {text =1865”, correct = false},
    {text =1945”, correct = false},
    },
    },
    -- more questions here
    }
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • antixantix +1 -1 (+1 / -0 )
    Member
    Got bored eating breakfast so this is kind of what I was eluding to above..
    local questions = {
    {
    question = "What year was the Declaration of Independence signed?",
    answers = {
    "1776", -- always put the correct answer first in the list
    "1917",
    "1812",
    "1865",
    "1945",
    },
    order = {}, -- the random order of the answers is stored here
    },
    -- more questions here
    }
     
    -- randomly order all questions
    local function randomizeAnswers(questions)
    local insert, remove, random = table.insert, table.remove, math.random
     
    for q = 1, #questions do
    local question = questions[q] -- next question to order randomly
     
    local available = {}
    local order = {} -- we will store our order here
     
    -- generate the default order
    local numQuestions = #question.answers
    for i = 1, numQuestions do
    insert(available, i)
    end
     
    -- generate the randomized order
    for i = 1, numQuestions do
    local n = random(1, #available) -- throw a random number from 1 to the number of available answers
    insert(order, remove(available, n)) -- remove the randomly chosen answer and add it to the order list
    end
     
    question.order = order -- save the new random order
    end
    end
     
    for i = 1, 9 do
    randomizeAnswers(questions) -- mix them up
     
    local question = questions[1] -- print out the results
    local order = question.order
    local s = ""
    for i = 1, #order do
    s = s .. order[i] .. ", "
    end
    print(s)
    end

    Likes: pie

    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor
  • antixantix +1 -1
    Member
    Actually in my example above you wouldn't even need to have the bool to signify which answer was correct. Instead always put the correct answer as the first in the answer list as the order they will be displayed will always be random :)

    EDIT: Updated the above code to support this theory ;)
    Check out my DevBlog, my GitHub, and my games Falling Animals | Breaky Wall | Exetor

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Login with Facebook Sign In with OpenID

In this Discussion

Top Posters