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.
- 8 questions from 1st group
- 5 questions from 2nd group
- 5 questions from 3rd group
- 6 questions from 4th group
- 6 questions from 5th group
- 5 questions from 6th group
- 9 questions from 7th group
- 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
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?
- Generate an array of the numbers Zero through 355 (arrays are zero-based).
- Shuffle that array. Lots of good examples out there - here's a fairly comprehensive reference: https://stackoverflow.com/a/24029847/6257435
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
0 comments:
Post a Comment