Shuffle/Randomize Questions from an Array with no Repetition (SWIFT) - arrays

I've been browsing for several hours to find the right answer but seems like I can't apply it properly to my own code.
Most of the time, the answers given on stackoverflow for this problem are with Strings & Int examples and I'm struggling with the Question Type.
I'm just starting on Xcode & Swift and managed so far to make a Quiz App that works perfectly fine.
I've used shuffle but I can't find the rest of the code not to repeat questions previously asked.
What I exactly want to do : I have 7 questions for now, I want those to be asked ONCE in different orders anytime someone wants to play the Quiz.
I've got two classes in 2 other swift files.
This is my QuestionBank type with 7 questions all completed properly:
class QuestionBank {
var list = [Question]()
init () {
list.append(Question(questionNumber: 0, image: "a", questionText: "a", choiceA: "a", choiceB: "a", choiceC: "a", choiceD: "a", answer: 1))
And this is my Question class :
}
class Question {
let number : Int
let questionImage: String
let question: String
let optionA: String
let optionB: String
let optionC: String
let optionD: String
let correctAnswer: Int
init(questionNumber: Int, image: String, questionText: String, choiceA: String, choiceB: String, choiceC: String, choiceD: String, answer: Int) {
number = questionNumber
questionImage = image
question = questionText
optionA = choiceA
optionB = choiceB
optionC = choiceC
optionD = choiceD
correctAnswer = answer
}
And the func to updateQuestion where I believe sh*t is supposed to happen but doesn't.
func updateQuestion() {
if questionNumber <= allQuestions.list.count - 1 {
imageView.image = UIImage(named:(allQuestions.list[questionNumber].questionImage))
QuestionLabel.text = allQuestions.list[questionNumber].question
optionA.setTitle(allQuestions.list[questionNumber].optionA, for: UIControl.State .normal)
optionB.setTitle(allQuestions.list[questionNumber].optionB, for: UIControl.State .normal)
optionC.setTitle(allQuestions.list[questionNumber].optionC, for: UIControl.State .normal)
optionD.setTitle(allQuestions.list[questionNumber].optionD, for: UIControl.State .normal)
selectedAnswer = allQuestions.list[questionNumber].correctAnswer
questionNumber += 1
allQuestions.list.shuffle()

You seem to be shuffling the list every time you call updateQuestion which seems to be the issue here. You are only supposed to call the shuffle once and iterate through the questions one by one. To fix the issue remove the shuffle from updateQuestion and add it in viewDidLoad or just call it once in updateQuestion based on a condition like this:
if questionNumber == 1 {
allQuestions.list.shuffle()
}

Related

Array Sorting - Swift How Can Sort A-Z [duplicate]

This question already has an answer here:
Swift sort an array with strings and numbers [duplicate]
(1 answer)
Closed 1 year ago.
I have an array of pdf files (see attached screenshot) that I need to sort alphabetically. Here is the code that generates the array, can anybody suggest changes to have the array sorted?
import UIKit
var pdfsArray = [String]()
class PDFListViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let fm = FileManager.default
let path = Bundle.main.resourcePath!
let items = try! fm.contentsOfDirectory(atPath: path)
for item in items {
if item.hasSuffix(".pdf") {
pdfsArray.append(item.uppercased())
}
let sortedFiles = pdfsArray.sorted()
pdfsArray = sortedFiles
}
Simple enough, you can just call swift's sort (or sorted) function and provide the sort-function to sort by numeric values:
var array = ["Part 45", "Part 1", "Part 10", "Part 20", "Part 100", "Part 2"]
array.sort { st1, st2 in
st1.compare(st2, options: .numeric) == .orderedAscending
}

Xcode Quiz look-a-like app with question and answer

I am trying to create a quiz look-a-like app, where the person who holds the phone ask the question, and the other people answer. So there will be two Strings. One with question, and one with the answer. I have created the questions something like this:
var questions = ["Question1", "Question2", "Question3", "Question4", "Question5"]
var answers = ["Answer1", "Answer2", "Answer3", "Answer4", "Answer5"]
When the tap a button, a new question with correct answer pops up. I know how I can display a random string from questions, but how do I connect it to also display the correct answer?
Another option is to use a Dictionary, with the Question as the Key and the Answer as the Value:
let questions: [String : String] = [
"Question1" : "Answer1",
"Question2" : "Answer2",
"Question3" : "Answer3",
"Question4" : "Answer4",
"Question5" : "Answer5"
]
You can then get a random Question & Answer like this:
let randomQuestion = questions.randomElement()
Then access the Question and Answer Text:
let questionText = randomQuestion?.key ?? ""
let answerText = randomQuestion?.value ?? ""
In relation to your next question:
How can I make sure the same question does not show multiple times, and when there are no more questions
You can construct an Array from the Dictionary Keys like this. The keys will be unordered anyway, but you should shuffle them if you want to repeat.
You can then iterate through each question in the randomised Array:
Set your properties in viewDidLoad, not when the button is tapped.
let randomQuestions = questions.keys.shuffled()
var currentQuestionIndex = 0
#IBAction func newQuestionButton(_ sender: Any) {
guard currentQuestionIndex != questions.count else {
return
// or reset your questionIndex and reshuffle.
}
// This will give you the Question (and Key)
let question = randomQuestions[currentQuestionIndex]
// Use the Key to extract out the answer (value) from the Dictionary
let answer = questions[question] ?? ""
// Update your labels
questionLabel.text = question
answerLabel.text = answer
// Increment your question index
currentQuestionIndex += 1
}
You can simply zip together questions and answers and then call randomElement on the result. This will give you a Tuple containing a random question and its respective answer - assuming the indices of questions and answers are in sync.
var questions = ["Question1", "Question2", "Question3", "Question4", "Question5"]
var answers = ["Answer1", "Answer2", "Answer3", "Answer4", "Answer5"]
let questionsAndAnswers = Array(zip(questions, answers))
let randomQA = questionsAndAnswers.randomElement()
You could create a QuizItem type like. Since you always need them together it is good practice to tie them together in one element instead of having two arrays.
struct QuizItem {
var question: String
var answer: String
}
Then you create and array (or list) [QuizItem] and add all items you want. At last you simply take random element of the array.
Here is some pseudo code:
var quizList = [QuizItem]()
quizList.append(...) // add questions & answers
let randomIndex = randomIndex between 0 and quizList.length-1
let item = quizList[randomIndex]
questionLable.text = item.question
answereLabel.text = item.answere

Why do my answers randomize in my Quiz Game?

I made a Quiz Game in Swift 2 last year, now I need to use it again when I converted it to Swift 3 the answers randomize now... Here is a sample Question Structure...
Questions = [Question(Question: "What is the Biggest Hit of Bing Crosby?" , Answers: ["Swinging on a Star", "Now is the Hour", "White Christmas", "Beautiful Dreamer"], Answer: 2),]
This is where I randomize the questions and put them into the labels
func PickQuestions() {
counter += 1
score += 1
scoreLbl.text = "\(score)"
restartBtn.isEnabled = false
if Questions.count > 0 && counter <= 15 {
QNumber = Int(arc4random_uniform(UInt32(Questions.count)))
QLabel.text = Questions[QNumber].Question
AnswerNumber = Questions[QNumber].Answer
for i in 0..<Buttons.count{
Buttons[i].setTitle(Questions[QNumber].Answers[i], for: UIControlState())
}
Questions.remove(at: QNumber)
}
}
I had to change the following line manually which may have caused an issue from...
QNumber = Int(arc4random_uniform(UInt32(Questions.count)))
to
QNumber = random() % Questions.count
Thanks
Arrays are unordered collections, meaning that their order could potentially change without your knowing. Your best bet would be to create a single array containing a struct that holds both the question and answer, like so:
struct QuizItem {
var question: String!
var answer: String!
var answerOptions: [String]!
init(q: String, a: String, aOptions:[String]) {
self.question = q
self.answer = a
}
}
Change your declaration to look like the following:
let items = [QuizItem(...)]
Your code like this:
func PickQuestions() {
counter += 1
score += 1
scoreLbl.text = "\(score)"
restartBtn.isEnabled = false
if items.count > 0 && counter <= 15 {
QNumber = Int(arc4random_uniform(UInt32(Questions.count)))
let item = items[QNumber]
QLabel.text = item.question
for i in 0..<Buttons.count{
Buttons[i].setTitle(item.answerOptions[i], for: UIControlState())
}
items.remove(at: QNumber)
}
}
Also, kind of picky but worth highlighting still. Swift is a camel case language and although this rule isn't set in stone, you should try and stick to the widely recognised coding practices.

How to get an array with Name stored as a string in swift

I have a program in which I have to make an string array equal to another array. The second array needs to be found by its name,
So for example something like this :
let StoreString1 = ["", ""]
let StoreString2 = ["", ""]
let FinalString = GetStringWithName("StoreString" + Number)
in C# its GetComponent("ComponentName");
Thanks for all answers, and sorry for the confusing way I wrote the question, because I didn’t really know hot to put it into words XD.
What you are trying to do can be achieved by using Dictionaries in swift. It appears that you are not familiar with this topic, so will be of no use to just throw out some code, there are many tutorials about this regard.
Just for mentioning one, (that has even images explaining how it works) you can enter here
Happy Learning & Coding! ;-)
UPDATE:
Here's a playground testing the concept: (Feel free to adjust it to your needs)
var dictionary: [String : [ String ]] = ["" : []]
let storeString = "StoreString"
func addUpdateArray(strings: [String], index: Int) {
let locator = storeString + index.description
dictionary[locator] = strings
}
func getStringWitNameIndex(index:Int) -> [String]? {
return dictionary[ storeString + index.description]
}
func addToArray(index:Int, inout destiny: [String]) {
if let array = getStringWitNameIndex(index) {
destiny = destiny + array
}
}
addUpdateArray(["Hello", "World"], 1)
addUpdateArray(["Hello", "Cats"], 3)
var finalArray : [String] = []
addToArray(1,&finalArray)
addToArray(3,&finalArray)
finalArray
In this case, finalArray ends up having: ["Hello", "World", "Hello",
"Cats"]

Extracting elements of a tuple from an array of tuples in swift

I have managed quite easily to extract tuple from an array of tuples however I am stumped as to how to extract the elements of the tuple.
//: Playground - noun: a place where people can play
import UIKit
import Foundation
class ActivityDetailsModel {
var activityCategory: String! // The choice of major activity
init(activityCategory: String){
self.activityCategory = activityCategory
}
class func activityForm(activityCategory: String) -> [(Question: String, Answer: String)] {
var activityProfile: [(Question: String, Answer: String)] = [] //An array of tuples containing the users activity details
switch activityCategory {
case "Sport":
activityProfile = [(Question: "Home Club", Answer: "a"),(Question: "Other venues", Answer: "b")]// [, ["Activity days – Mon – Sunday": "c"], ["Player strength": "d"], ["Age group to play with": "e"]]
return activityProfile
case "Recreation":
activityProfile = [(Question: "Home Club2", Answer: "ab"),(Question: "Other venues3", Answer: "bc")]
return activityProfile
default:
var activityProfile = [(Question: "nixs", Answer: "nie")]
return activityProfile
}
}
}
var actProf:[(Question: String, Answer: String)]
actProf = ActivityDetailsModel.activityForm("Recreation")
println(actProf[1])
This returns the second tuple from the array - all I need now is to extract the elements of the tuple
let question = actProf[1].0
let answer = actProf[1].1
will give you the elements from the tuple.
As the items are named properly, very simple
actProf[1].Question
alternatively
actProf[1].0
or
actProf[1].Answer
alternatively
actProf[1].1
Sorry for asking this - the answer occurred to me 5 min. later - it was as simple as
var tupOut = actProf[1]
tupOut.Question
Thanks to anyone who has had a look
To get both in one line:
let (question, answer) = ActivityDetailsModel.activityForm("Recreation")[0]

Resources