Sunday, November 12, 2017

Quiz app to pick questions from 8 group of questions randomly?

Leave a Comment

I build an application for iOS and I want it to pick questions from 8 different groups of questions and randomly. The number of questions is 356 and only 50 of them must be picked randomly.

  1. 8 questions from 1st group
  2. 5 questions from 2nd group
  3. 5 questions from 3rd group
  4. 6 questions from 4th group
  5. 6 questions from 5th group
  6. 5 questions from 6th group
  7. 9 questions from 7th group
  8. 6 questions from 8th group

Total: 50 questions.

What i did is that, i build the quiz, but it reads all the questions and shows them to the user. The questions are read from a .json file.

Here is my code: (Because there are no comments on the code, ask me.)

import UIKit  struct Question {     var Question: String!     var Answers: [String]!     var Answer: Int!      init(item: [String: Any])     {         self.Question = item["Question"] as? String         self.Answers = item["Answers"] as? [String]         self.Answer = item["Answer"] as? Int     } }   class LittleTestViewController: UIViewController {      //MARK: Properties     @IBOutlet weak var questionLabel: UILabel!     @IBOutlet var buttons: [UIButton]!      var Questions = [Question]()     var QNumber = Int()     var answerNumber = Int()  override func viewDidLoad() {         super.viewDidLoad()          jsonParsingQuestionsFile()         pickQuestion()     }      func jsonParsingQuestionsFile ()     {         guard let path = Bundle.main.path(forResource: "data", ofType: "json"),             let array = (try? JSONSerialization.jsonObject(with: Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe), options: JSONSerialization.ReadingOptions.allowFragments)) as? [[String : Any]] else{                 return         }         for item in array         {             self.Questions.append(Question(item: item))         }     }      func pickQuestion ()     {         if Questions.count > 0 {             QNumber = 0             questionLabel.text = Questions[QNumber].Question              answerNumber = Questions[QNumber].Answer              for i in 0..<buttons.count{                 buttons[i].setTitle(Questions[QNumber].Answers[i], for: UIControlState.normal)             }             Questions.remove(at: QNumber)         }         else         {            print ("End")         }     }  @IBAction func btn1(_ sender: UIButton){         Unhide()         if answerNumber == 0 {             print ("Correct!!")         }         else         {          }     }      @IBAction func btn2(_ sender: UIButton) {         Unhide()         if answerNumber == 1 {            print ("Correct!!")         }         else         {          }     }     @IBAction func btn3(_ sender: UIButton) {         Unhide()         if answerNumber == 2 {             print ("Correct!!")         }         else         {          }     }     @IBAction func btn4(_ sender: UIButton) {         Unhide()         if answerNumber == 3 {             print ("Correct!!")         }         else         {          }     } } 

data.json

[ {"Question":"Group 1. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 1. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 1. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 3},   {"Question":"Group 2. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 2. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 1},   {"Question":"Group 2. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 3. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 3. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 4. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 4. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 3},   {"Question":"Group 4. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 5. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 0},   {"Question":"Group 5. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 6. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 0},   {"Question":"Group 7. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},   {"Question":"Group 8. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 0},   {"Question":"Group 8. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 1},   {"Question":"Group 9. lalala?", "Answers":["lala","trtr","asas","bbbb"],"Answer": 2},] 

This .json file is just an example there are more questions for every group. Every group has about 45 questions.

4 Answers

Answers 1

Don't mess your logic with code.

Follow this if you're concerned about Time Complexity - Difficult to implement

Just follow the algorithm below.

Since you have 8 groups create an integer array that is able to hold up to 8 values and initialize with all 0's

Array(1)  =   [0 0 0 0 0 0 0 0] 

And create another array that holds maximum questions that can be drawn from each group it looks like

Array(2)  =   [8 5 5 6 6 5 9 6] 

Also, there should be an array for keeping number of questions remaining in each group

n[]   =  [ 8elements ] not mensioned in question 

Lets start

Loop

1) Create a random number between 1-8 to choose the group

2) Check if the group has the maximum questions chosen already. By comparing array(1) and array(2) arrays if yes do step 1 once again.

3) Let maximum questions in a randomly selected group shall be n. Generate a random number between 1-n. Increase 1 to the corresponding position in the array(1) and reduce n by 1. Delete the selected question from group Check if you reached 50 questions If yes, quit If no, do step 4

4) Save your randomly selected question somewhere. And continue to step 1

It's better you append 50 questions into an array and put into the UI.

This is when you don't consider complexity - Simple to implement

For this, you need to shuffle all the group of questions

Refer this post shuffle

Then take first set of questions from each of groups(how much you want). It will be random.

Advice to you: First solve the problem, then write code!

Answers 2

I would suggest you to make a new .json file with 8 groups of questions in 8 different groups, like questions1, questions2, etc.. after that with a switch make 8 cases and break every case if the number of questions picked randomly is the number you want for every group.

Answers 3

Thanks for the advices guys. But I found the way on how to do it. I only changed the pickQuestion function.

 func pickQuestion () {     if Questions.count > 0 && questionscount < 8{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("KEK")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("KEK")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions[QNumber].Answers[i], for: UIControlState.normal)         }         print(QNumber)         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 13{          QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("M")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("M")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("M")}[QNumber].Answer           for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("M")}[QNumber].Answers[i], for: UIControlState.normal)         }         print(QNumber)         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 18{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("N")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("N")}[QNumber].Question           self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("N")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("N")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 24{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("A")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("A")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("A")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("A")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 30{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("ΑΔ")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("ΑΔ")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("ΑΔ")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("ΑΔ")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 35{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("ΕΠ")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("ΕΠ")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("ΕΠ")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("ΕΠ")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 44{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("T")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("T")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("T")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("T")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else if Questions.count > 0 && questionscount < 50{         QNumber = Int(arc4random_uniform(UInt32(Questions.filter{$0.Question.hasPrefix("ΑΝΘ")}.count)))         questionscount += 1         questionLabel.text = Questions.filter{$0.Question.hasPrefix("ΑΝΘ")}[QNumber].Question          self.title = "Ερώτηση: \(Int(questionscount))/50"         answerNumber = Questions.filter{$0.Question.hasPrefix("ΑΝΘ")}[QNumber].Answer          for i in 0..<buttons.count{             buttons[i].setTitle(Questions.filter{$0.Question.hasPrefix("ΑΝΘ")}[QNumber].Answers[i], for: UIControlState.normal)         }         Questions.remove(at: QNumber)      }else     {         let alert = UIAlertController(title: "Σκόρ", message: "Απάντησες σωστά τις \(Int(score)) από τις \(Int(questionscount)) ερωτήσεις! \n \(String(format: "%.0f",(score/questionscount*100))) %", preferredStyle: UIAlertControllerStyle.alert)         alert.addAction(UIAlertAction(title: "Μενού", style: UIAlertActionStyle.default, handler: { action in             self.navigationController?.popToRootViewController(animated: true)         }))         self.present(alert, animated: true, completion: nil)     }     Hide() } 

Answers 4

Based on your data.json, you are receiving a single list of 356 questions. The "8 groups* are simply group designations, with one "property" of each "question" being the group.

If you are actually receiving the data in groups of questions, then first step is to append them together into one group as you show here.

So, how to show 50 random questions, from that list of 356 questions?

The first 50 elements of your array now contain random (no duplicates) values between 0 and 355, so you can simply show the actual questions in that order.


Edit:

If the requirement includes a pre-determined number of questions per group, that only adds a couple more steps:

Instead of using One big array of all 356 questions, split your json data into 8 arrays, each containing the questions belonging to a specific group.

Then, add those arrays to a two-dimensional array of "groups".

Next, build a two-dimensional array of question "ID" numbers - simply a sequence of 0 through number of questions in each group - and then shuffle each array.

The result will be X-number of random questions from each group.

Here is a simple example that you can run in a Playground page. The code generates a arrays of Question objects, and corresponding shuffled arrays of "indexes". Tapping the "Next Question" button will step through the result.


import UIKit import PlaygroundSupport  // simple random number function func random(_ range:Range<Int>) -> Int {     return range.lowerBound + Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound))) }  // shuffling extension extension MutableCollection where Indices.Iterator.Element == Index {     /// Shuffles the contents of this collection.     mutating func shuffle() {         let c = count         guard c > 1 else { return }          for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {             let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))             guard d != 0 else { continue }             let i = index(firstUnshuffled, offsetBy: d)             swap(&self[firstUnshuffled], &self[i])         }     } }  extension Sequence {     /// Returns an array with the contents of this sequence, shuffled.     func shuffled() -> [Iterator.Element] {         var result = Array(self)         result.shuffle()         return result     } }  // Question object struct Question {     var Question: String!     var Answers: [String]!     var Answer: Int!      init(item: [String: Any])     {         self.Question = item["Question"] as? String         self.Answers = item["Answers"] as? [String]         self.Answer = item["Answer"] as? Int     } }  // standard UIViewController class TestViewController : UIViewController {      // standard UIButton     let btn: UIButton = {         let b = UIButton()         b.setTitle("  Next Question  ", for: .normal)         b.backgroundColor = .red         b.translatesAutoresizingMaskIntoConstraints = false         return b     }()      // standard UILabel     let questionLabel: UILabel = {         let v = UILabel()         v.backgroundColor = .white         v.numberOfLines = 0         v.translatesAutoresizingMaskIntoConstraints = false         return v     }()      // two-dimensional array of Question objects     var arrayOfQuestions = [[Question]]()      // two-dimension array of shuffled Index values     var arrayOfIDs = [[Int]]()      // number of questions per group     let questionsPerGroup = [8, 5, 5, 6, 6, 5, 9, 6]      // note: arrays are Zero-based,      //  so "first question" index will be Zero     //  and "first group" index will be Zero      // start Question counter at -1 so "next question" will be Zero     var currentQuestion: Int = -1      // group counter     var currentGroup: Int = 0      override func viewDidLoad() {         super.viewDidLoad()          // this is just generating 8 groups of Questions that look like:         // {"Question":"Group: 1 Question: 1", "Answers":["A", "B", "C", "D"], "Answer":1}          for iGroup in 1...8 {              //  each group will have between 43 and 48 questions             let numQuestions = random(43..<49)              // new empty array             var aGroup = [Question]()              for iQuestion in 1...numQuestions {                  let s: [String:Any] = [                     "Question":"Group: \(iGroup) Question: \(iQuestion)",                     "Answers":["A", "B", "C", "D"],                     "Answer": random(0..<3)                 ]                  let q = Question(item: s)                  aGroup.append(q)              }              // append this "group" to the array             arrayOfQuestions.append(aGroup)              // create array of numbers 0 through number of questions -1             let aIDs = Array(0..<numQuestions)              // shuffle that array and append to "IDs" array             arrayOfIDs.append(aIDs.shuffled())          }          // add a button and label to the view         self.view.addSubview(btn)         self.view.addSubview(questionLabel)          btn.topAnchor.constraint(equalTo: view.topAnchor, constant: 40.0).isActive = true         btn.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0.0).isActive = true          questionLabel.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0).isActive = true         questionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16.0).isActive = true         questionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16.0).isActive = true          // add touch up target to the button         btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)          // show the first question         showNextQuestion()      }      func showNextQuestion() -> Void {          // increment currentQuestion         currentQuestion += 1          // if we are past the number of questions in this group         //  set currentQuestion back to Zero and increment currentGroup         if currentQuestion == questionsPerGroup[currentGroup] {             currentQuestion = 0             currentGroup += 1         }          // if we are past the last group, show a message         if currentGroup == questionsPerGroup.count {             questionLabel.text = "End of quiz"         } else {              // get the question ID from the shuffled IDs of the current group             let idx = arrayOfIDs[currentGroup][currentQuestion]              // get that question object from the array             let thisQuestion = arrayOfQuestions[currentGroup][idx]              // get the question parts             let sQuestion = thisQuestion.Question ?? "missing data"             let aAnswers = thisQuestion.Answers ?? ["missing data"]             let iAnswer = thisQuestion.Answer ?? -1              // show them in our label             questionLabel.text = "\nThis is Question: \(currentQuestion + 1) of Group: \(currentGroup + 1)\n\n" +                 "\(sQuestion)\n" +                 "\(aAnswers)\n" +                 "\(iAnswer)\n"         }      }      func didTap(_ sender: Any?) -> Void {         showNextQuestion()     }  }  let vc = TestViewController() vc.view.backgroundColor = .blue PlaygroundPage.current.liveView = vc 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment