WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Day 35 Challenge

Forums > 100 Days of SwiftUI

Hello,

I made it to day 35 of the Hacking with SwiftUI course and I am stuck. For those that don't remember the challenge involves making an edutcation app for multiplication tables. The part that I am sturggling with is generating the questions. Perhaps I am approaching this the wrong way, hence the post. What I am trying to do is creating a Struct that takes 2 inputs, the type of multiplication table and number of questions, and based on these inputs it generates a dictionary where the questions are the keys and the values are the answers, something like:

{
  "What is 5 X 5?": 25,
  "What is 5 X 2?": 10
}

I have started creating it so:

struct QuestionBank {
    let multiplicationTable: Int
    let numberOfQuestions: Int

    var allQuestions = [String: Int]()

    mutating func generateQuestionBank() {
        for i in 1...numberOfQuestions {
            // append to allQuestions
        }
    }
}

However this causes a few problems, first of all allQuestions becomes an input variable, I tried to switch to a computed property but that didn't really work. Is there anything I could try to get this to work?

1      

Andy asks:

What I am trying to do is creating a Struct that takes 2 inputs, the type of multiplication table and number of questions, and based on these inputs it generates a dictionary where the questions are the keys and the values are the answers....

Excellent question! Also +1 for taking a BIG problem and breaking it down into several smaller problems.

Big Problem: Create a multi-purpose Multiplication Table struct. Small Problem 1: Accept number of question and Multiplication table value.

Analysis:

struct QuestionBank {
    let multiplicationTable: Int  // accept user’s selected table
    let numberOfQuestions: Int // accept user’s desire for punishment
// ………. snip ………….

Looks like Small Problem 1 is complete.

Small Problem 2: Generate Questions Analysis:

var allQuestions = [String: Int]()  // create an empty dictionary
mutating func generateQuestionBank() {
    for _ in 1...numberOfQuestions {
        // what to do here?
       // BREAK your big problem into several smaller problems!
    }

You have another big problem, you need to break into smaller problems. What parts do you need to solve your big problem?
You probably need the QUESTION, which is made up of two parts, the given times table, and a random integer. Then you want to pack these into a string. Of course, you also want the answer. These smaller parts are then packed into a dictionary.

let questionInt = Int.random(0…12) // pick a random number between 1 and 12.
let question = "\(multiplicationTable) x \(questionInt) = "  // a string looks like  “6 x 9 =“
let answer = questionInt * multiplicationTable
// Now you have the smaller problems solved, assemble them to solve your larger problem.
allQuesions[question] = answer  // this is how you add something to a dictionary.

Here’s sample code to run in Playgrounds:

struct QuestionBank {
    let multiplicationTable: Int
    let numberOfQuestions: Int

    var allQuestions = [String: Int]()

    mutating func generateQuestionBank() {
        for _ in 1...numberOfQuestions {
            let questionInt = Int.random(in: 0...12) // multiply multiplicationTable x questionInt
            let question = "\(multiplicationTable) x \(questionInt) = "  // a string
            let answer = questionInt * multiplicationTable
            allQuestions[question] = answer  // add to the dictionary.
        }
    }
}

var sixTimesTable = QuestionBank(multiplicationTable: 6, numberOfQuestions: 10)
sixTimesTable.generateQuestionBank()

var nineTimesTable = QuestionBank(multiplicationTable: 9, numberOfQuestions: 5)
nineTimesTable.generateQuestionBank()

By breaking your big problem into smaller solvable problems, you’ve found a solution.

Or have you?

When you review the output, are you concerned? It seems the dictionary might have duplicate entries in it.

Does this concern you? Take a crack at this using your ideas. Move forward! But please post your final code here, if you’d like additional comments.

2      

Thank you for the breakdown and explanation. I was guessing it was going to look like that but wasn't sure how to start (I come from a Python background which is not really a Type-Safe language). I applied the Struct to my code and now it works and I am able to generate questions based on the user input... however this resulted in other issues I did not plan for.

A dictionary is unordered so I cannot go through it one-by-one displaying a question then moving on to the next. I will spend some time now trying to figure out how to work around this, maybe I will need to change the logic a bit.

Anyway, until then, just wanted to say Thank You once again for the assistance in helping me get past the first hurdle :)

1      

Andy shares experience with Python:

A dictionary is unordered so I cannot go through it one-by-one displaying a question then moving on to the next. I will spend some time now trying to figure out how to work around this, maybe I will need to change the logic a bit.

Something to think about.... Stepping through an unordered dictionary element by element, excluding duplicates, and moving to the next, then attempting to push bits to a display is very procedural. Swift is a functional programming language. Try a different tactic.

What is your phone doing?

What is your phone doing right now? Processing temperature? Listening for "Hey Siri!"...determining your latitude and longitude.... counting your steps....checking battery status...syncing with iCloud.... checking for new email messages....calculating 45,000 positions of your face for authentication...

It's doing exhausting work, but not even breaking a sweat!

So consider, hear me out, consider doing something outrageou! Why not create a bank array that contains Question data structures for each times table element from 1 up to 12. Your iPhone will probably be insulted when asked to perform such a trivial task. Pffffft.

Then, if the user just asks for 9 quiz questions, ask your bank array to return 9 random elements and load them into a quiz array? Base your user's interface off the quiz array, not the bank array. If your user changes their mind and asks for 6 quiz questions, it's easy to recalculate your quiz array. And your interface will update immediately to reflect the new contents of the quiz array.

Have a look in the documentation for array! Is there a method to shuffle the contents of an array? Is there a way to return the first X elements? Luke! USE the FORCE FUNCTIONS!

See: Swift Array Documentation

PS: I know! There are lots of reasons why Swift shouldn't be considered a functional programming language. Just humor me.

1      

I am currently working on this challenge and have found the discussion very useful.

Some additional question I have are:

1) I have created a Questions struct to create a bank array of questions with answers which is separate from the ContentView struct.

struct Questions {
    var questionsArray = [String]()

    mutating func buildQuestionsArray() {
        for i in 2...12 {
            for j in 1...12 {
                questionsArray.append("What is \(i) times \(j)?")
                questionsArray.append("The answer is \(i * j).")
            }
        }
    }
}

How do I create the bank array so it is available in the ContentView struct? If I try to create it in ContentView I get the below errors...

struct ContentView: View {
    @State private var tableSelected = 2
    @State private var questionOptions = [5, 10, 20]
    @State private var questionsWanted = 5
    @State private var gameActive = false
    @State private var answer = ""
    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var scoreTitle = ""
    @State private var showingAlert = false
    @State private var showingScore = false
    @State private var userScore = 0

    var myTimesTable = Questions()
    myTimesTable.buildQuestionsArray()     <-------Invalid redeclaration of 'myTimesTable()'

    var body: some View {
            Form {
                Section {
                    Stepper("\(tableSelected)", value: $tableSelected, in: 2...12)

2) How to extract the question and answers into a quiz array that matches user settings?

If the user selects, for example, review of the 3 times table and 5 questions and I extract the entire 3 times table questions and answers (I know their positions in the bank array) I simply can't shuffle the quiz array because the questions and associated answers are sequential in the quiz array. Paul hints on using a state property called questionNumber or similar, which is an integer pointing at some position in your question array, but I'm struggling how to make that work.

Any insights would be much appreciated.

   

Hacking with Swift is sponsored by Emerge

SPONSORED Why are Swift reference types bad for app startup time, and what’s the performance cost of protocol conformances? That’s just a couple of the topics you can learn about on the Emerge blog — written by the app performance experts behind Emerge’s advanced app optimization and monitoring tools, based on their experience of working at companies like Apple, Airbnb, Snap, and Spotify.

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.