If statements with arrays in swift - ios

I am having some trouble with my swift code. I want to make an endless game similar to the line zen, where a random node appears from the top. I used this code to help use the randomizer:
let randomWallNameIndex = Int(arc4random_uniform(2))
let wallNames = ["obsticle #1", "obsticle #2"]
//can also be [1, 2,]
if wallNames == "obsticle #1"{
//insert code here
}
Although, I'm having trouble using the if statement to tell whether a specific number was selected and I can spawn that certain node.
Can someone find the solution?

let randomWallNameIndex = Int(arc4random_uniform(2))
let wallNames = ["obsticle #1", "obsticle #2"]
//can also be [1, 2,]
let wall = wallNames[randomWallNameIndex]
if wall == "obsticle #1"{
//insert code here
}
Just add a variable that holds the string from the array at that index and use that variable in the if statement.

Related

Checking if array is nil or not [duplicate]

This question already has answers here:
Check if optional array is empty
(8 answers)
Closed 4 years ago.
I have a array of my custom model, and I want to check if it is not nil and its size is greater then 0.
Following is my array with custom object
var listCountries : [Countries]? = nil
now In viewDIdLoad I want to make a check on it. I am new to Swift. I have good experience in working in Java.
I have read out Optional values concept and guard, if let statements. But I am unable to understand how efficiently they may be used. I have read too much SO questions but failed to figure out.
for example , if I want to check the upper given array in java I have only to do
if(listCountries != null && listCountries.size()>0){
//DO something
}
So to summarize my question:
How to make the upper given(Java code) check in to swift 4.? What is more smooth and reliable way.
What is a use of if let , guard, guard let statements. if I declare a variable (array, string) as optional I have to bear optional check like force wrapping each and every place. This is for me so making too much confusion.
Please help. I know this question has been asked in different ways. But this has some different context.
Just use ??.
if !(listCountries ?? []).isEmpty {
However, since you want to probably use listCountries in the if block, you should unwrap
if let listCountries = self.listCountries, !listCountries.isEmpty {
Ideally, if nil and empty means the same to you, don't even use an optional:
var listCountries: [Countries] = []
I would do it something like...
if let list = listCountries, !list.isEmpty { // Can also use list.count > 0
// do something
}
Even though you are not using the list inside the braces you are still using the list in the condition.
Or, like Sulthan said... make it non-optional to begin with if it makes no difference.
Obviously, I would assume that you are able to recognize the difference between nil array and empty array.
So, if we tried to implement a literal translation to your question:
I want to check if it is not nil and its size is greater then 0
For the first condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// ...
}
and for the second condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// "and its size is greater then 0":
if !unwrappedList.isEmpty {
// ...
}
}
However, you could combine both of the conditions by using the comma to achieve the multi-clause condition:
// I want to check if it is not nil and its size is greater then 0
if let unwrappedList = listCountries, !unwrappedList.isEmpty {
// ...
}
Or by using guard statement:
// I want to check if it is not nil and its size is greater then 0
guard let unwrappedList = listCountries, !unwrappedList.isEmpty else {
return
}
if let list = listCountries {
if(!list.isEmpty && list.count > 0) {
//value
}
}

index(where:) method in swift is producing the wrong index

I have received an array index out of range error. I have two arrays cardsCurrentlyInPlay and currentCardsSelected. Every card that is in this game has a unique ID. I am attempting to find find the index of the card in cardsCurrentlyInPlay whose cardID matches the cardID of the card in currentCardsSelected. I am doing this by using the index(where:) method that takes a closure. My closure just checks if the IDs match, they obviously match because I am using the ! to unwrap them and the app does not crash there. It seems as though the index(where:) method is returning the wrong index. I have looked at this for hours and I do not understand whats going on.
Heres the code:
let indexOfFirstCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[0].cardID)}))!
let indexOfSecondCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[1].cardID)}))!
let indexOfThirdCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[2].cardID)}))!
if deck.isEmpty && selectedCardsMakeASet() {
/* Remove the old cards */
cardsCurrentlyInPlay.remove(at: indexOfFirstCard)
cardsCurrentlyInPlay.remove(at: indexOfSecondCard)
cardsCurrentlyInPlay.remove(at: indexOfThirdCard) // where code is blowing up
currentCardsSelected.removeAll()
/* Return indicies of cards to clear from the UI */
return .deckIsEmpty(indexOfFirstCard, indexOfSecondCard, indexOfThirdCard)
}
The index you’re getting is correct when your get it, but it becomes wrong when you remove other cards. Consider:
var a = ["x", "y", "z"]
let indexOfX = a.index(of: "x")! // returns 0
let indexOfZ = a.index(of: "z")! // returns 2
a.remove(at: indexOfX) // removes "x"; now a = ["y", "z"]
a.remove(at: indexOfZ) // index 2 is now out of bounds
You could interleave the calls to index(of:) and remove(at:), but a better approach would be to remove all three cards in a single pass, something like this:
let selectedCardIDs = currentCardsSelected.map { $0.cardID }
cardsCurrentlyInPlay = cardsCurrentlyInPlay.filter { card in
!selectedCardIDs.contains(card.cardID)
}
Note that this has the added benefit of avoiding the force unwrap, a sign of sounder logic.
This is because of out of bounds.
After the first two code excuted.
cardsCurrentlyInPlay.remove(at: indexOfFirstCard) &
cardsCurrentlyInPlay.remove(at: indexOfSecondCard)
there is only one element in the cardsCurrentlyInPlay .
Then if you excute cardsCurrentlyInPlay.remove(at: indexOfThirdCard), the program will crash.

Putting array into label in swift

#IBAction func generateBtn(sender: UIButton) {
let strt = UInt32(strtNum.text!)
let end = UInt32(endNum.text!)
let ttlNums = Int(amtNums.text!)
let x = RandomNum()
var z = 0
while z<ttlNums{
let y = x.rndNumGen(strt!, end: end!)
z += 1
var h = [String]()
h.append(String(y))
let display:String = h.joinWithSeparator(", ")
winningNums.text = display
print (display)
}
}
I don't know what is wrong with this code. I am trying to put the string display into the label and it prints out the last number from the random number generator. When i print it to the console it shows all of the random numbers.
The primary issue here is that your array is created fresh in every loop iteration, and your label is being set in every loop iteration. That means that the array will only ever contain the element made in that iteration, after which it's reset to a new array, and a new element is added. The array needs to be initialized once at the start, and have elements added to it repeatedly in the loop, then put into the label once at the end.
#IBAction func generateBtn(sender: UIButton) {
guard let startText = strtNum.text, let start = UInt32(startText),
let endText = endNum.text, let end = UInt32(endText),
let ttlText = amtNums.text, let ttlNums = UInt32(ttlText) else {
//one of these is nil, handle it gracefully here
return
}
let randomGenerator = RandomNum()
var h = [String]()
h.reserveCapacity(ttlNums)
for _ in 0..<ttlNums {
let randomNum = randomGenerator.rndNumGen(start, end: end)
h.append(String(RandomNum))
}
let display = h.joinWithSeparator(", ")
winningNums.text = display
print(display)
}
I've made a few other changes to bring this code in line with Swift best practices and conventions:
Don't force unwrap. Use an if let or guard let binding to safely handle nil values.
Give your variables meaningful names. Avoid single-letter names except in specific instances.
Don't put spaces beside a function/method name and the proceeding brackets.
Don't use a while loop to iterate over a known range. Instead, use a for in loop.
Dn't type in t3xtspk, it mks ur code look lik an angsty teenagr wrote it. Autocomplete will finish off words for you, so you barely end up typing anyway. Make it easy and readable.
I would suggest you make a few changes yourself:
Rename generateBtn. Functions/methods DO things, they're actions. They should be named with verbs, or verb phrases. Perhaps try something like displayRandomArray.
Refactor the random array generation into its own method.
Rename RandomNum. By the looks of it, it's not a number at all, it's a random number generator. Perhaps try RandomNumberGenerator
Rename h.
Add code to deal with what happens when the .text is nil, or what happens when it contains a string that isn't a UInt32 (thus causing the UInt32 initializer to fail and return nil)

Displaying random questions by looping through a dictionary

I started to learn swift by trying to make a quiz game. But i'm stuck at trying to display random questions from an dictionary. The reason why i use a dictionary is because the quiz i'm working on is based on chemistry elements which has different symbols.
So Based on the selected difficulty i created four different dictionaries:
//Defining the dictionaries per difficulty
var easy: [String: String] = [
"H": "Waterstof",
"He": "Helium",
"Li": "Lithium",
"B": "Boor",
"C": "Koolstof",
"O": "Zuurstof",
"Ne": "Neon",
"Al": "Aliminium",
"Si": "Silicium",
"K": "Kalium",
"Fe": "Ijzer",
"Ni": "Nikkel",
"Zn": "Zink",
"Cd": "Cadmium",
"I": "Jood"
]
var normal: [String: String] = [
"N": "Stikstof",
"F": "Fluor",
"Na": "Natrium",
"Mg": "Magnesium",
"P": "Fosfor",
"CI": "Chloor",
"Ar": "Argon",
"S": "Zwavel",
"Ca": "Calcium",
"Cu": "Koper",
"Au": "Goud",
"Br": "Broom",
"Ag": "Zilver",
"Pt": "Platina",
"Ba": "Barium"
]
and so on.. (hard, extreme)
This is my viewDidLoad() method:
override func viewDidLoad() {
switch (self.selectedDifficultyByUser){
case "Makkelijk":// Means 'easy' in dutch
//Assigning the easy dictionary to a temporary dictionary
self.temp = self.easy
case "Normaal":
//Assigning the normal dictionary to a temporary dictionary
self.temp = self.normal
case "Moeilijk":
//Assigning the hard dictionary to a temporary dictionary
self.temp = self.moeilijk
case "Extreem":
//Assigning the extreme dictionary to a temporary dictionary
self.temp = self.extreem
default:
println("Something went wrong")
}
super.viewDidLoad()
self.answers = [self.btn1, self.btn2, self.btn3, self.btn4]
pickRandomElement()
randomizeAnswers()
let updateTime : Selector = "updateTime"
timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: updateTime, userInfo: nil, repeats: true)
startTime = NSDate.timeIntervalSinceReferenceDate()
}
As you can see i'm assigning all my buttons to the array 'answers' which i will be looping through soon.
The function pickRandomElement will then be called, which looks like this:
func pickRandomElement() -> () {
//First here we make sure that the 'answerOptions' Array is cleared,
//Because it could cause that the correct answer won't be showed.
answerOptions = [String]()
if self.selectedDifficultyByUser == "Makkelijk" {
let index: Int = Int(arc4random_uniform(UInt32(easy.count)))
let symbol = Array(easy.keys)[index]
let element = Array(easy.values)[index]
self.currentQuestion = symbol
self.correctAnswer = element
//Assign the correctAnswer to the answerOptions array
self.answerOptions.append(element)
//Show the question to the user
self.questionLabel.text = self.currentQuestion
//remove the correctanswer from the dictionary, to
//make sure that the answers won't be duplicated
self.easy[symbol] = nil
self.easy[element] = nil
for (var i = 0; i < 3; i++) {
let randomIndex: Int = Int(arc4random_uniform(UInt32(easy.count)))
let optionSymbol = Array(easy.keys)[randomIndex]
let optionElement = Array(easy.values)[randomIndex]
self.answerOptions.append(optionElement)
//Removing 'optionSymbol' and 'optionElement' from the array
//to prevent duplicated answer
self.easy[optionElement] = nil
self.easy[optionSymbol] = nil
}
self.easy = self.temp //Filling the 'easy' array with the temporary array
}
}
The problem i'm having is in the for loop shown above. I'm looping three times to pick random elements and show them to the user. And during this process i'm deleting the randomly chosen elements from the (easy) array to prevent duplicate answer.
And because i'm removing those elements from the easy array i'm assigning the temporary array, which i created in the begin, back to the easy array.
If i don't do this, the whole easy array will be empty after three rounds or so.
If i do the opposite i will get an infinte loop.
Can someone please put me in the right direction if i'm doing this the wrong way or help me out of this problem?
Thanks in advance!
UPDATED
Firo's solution worked perfectly for me. But i'm know faced with another bug. Namely, the pickRandomElement function returns sometimes the asked question more than once. I tried to make another dictionary and put the asked questions in there and check to see if that question has already been asked. However, as the number of questions answered increases, this would require a linearly increasing amount of lookups for each question asked, until all lookups is reached. Does someone know how i can take care of this problem? –
So without all of your code I can only give you an educated guess as to a good way of handing your infinite loop situation with your current code.
An easy way to prevent an infinite loop here is to verify you still have remaining questions you want to ask. Keep an instance value to check how many questions you still want to ask.
var remainingSteps = 5
Then when the user answers the correct question, check it before presenting the next question
func userAnsweredQuestionCorrectly() {
if self.remainingSteps > 0 {
// Present next question
// Decrement remaining steps
self.remainingSteps--
else {
// Done asking questions, back to menu (or whatever is next)
}
}
Firo's solution is awesome. This one might help you with getting a unique question each time to avoid repetition. Let me know here or on codementor if you have any questions.

Basic Xcode - reference in random number generator not working

I am working on a very basic (I think) program in Xcode. I am trying to write an app for "drawing straws" where the last person who is chosen is the loser. There are a few things I'd like to do. First, see the code below:
import UIKit
let players = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
var draw = Int(arc4random_uniform(4))
playerNames.removeAtIndex(draw)
print(playerNames)
let round2 = playerNames.count
var draw2 = Int(arc4random_uniform(2))
playerNames.removeAtIndex(draw2)
print(playerNames)
let round3 = playerNames.count
var draw3 = Int(arc4random_uniform(1))
playerNames.removeAtIndex(draw3)
print(playerNames)
The first thing that's wrong is I'm currently hard-coding the random integer being drawn in var draw = Int(arc4random_uniform(4)). When I try to reference players instead of just typing in 4, I get an error. Can someone please help explain the problem there?
I'll stop there for now to see if I can fix that, and I'll wait until that is fixed before posting a new question. Thank you.
The function arc4random_uniform() takes a UInt32, so you need to declare the variable players as UInt32
let players: UInt32 = 4
var draw = (arc4random_uniform(players))
For handling the round variables, you would cast the count to UInt32.
let round2 = UInt32(playerNames.count)
You can also refactor your code
let players: UInt32 = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
println(playerNames)
for loop in 1...players {
RemovePlayer(&playerNames)
println(playerNames)
}
And the code for the RemovePlayer function
func RemovePlayer(inout names: [String]) {
// get the number of names in the array
var maxNames = UInt32(names.count)
var draw = Int((arc4random_uniform(maxNames)))
names.removeAtIndex(draw)
}
As Blackfrog already stated your randomization function requires a UInt32
Since you stated that you just started programing I would like to give you some other advice. Your code can be written as:
var playerNames = ["John", "Tyler", "Pete", "Dave"]
for var index = playerNames.count ; index > 1 ; index -= 1{
var random:Int = Int(arc4random_uniform(UInt32(index)))
playerNames.removeAtIndex(random)
}
println(playerNames[0])
This has a few advantages. At the moment you are hardcoding the values for how many players join a game. You will need to re-write an unnecessary amount of code if you want to add a player.
Let’s see what the above code does:
We start by declaring the playerNames adding a type declaration of [String] isn’t necessary, swift already knows that it is this type by its initial value.
for var index = playerNames.count; index > 1 ; index -= 1{
We create a new variable called index this will be set to the amount of items in the playerNames array (4 in this case)
After this we will declare that we want the loop as long as our index is greater than 1 this will make it run 3 times and make sure we are left with 1 item in our array
The index -=1 will subtract 1 from the index after each iteration through the loop.
var random:Int = Int(arc4random_uniform(UInt32(index)))
Here we declare a new variable random this will be of type Integer. Let’s work from the inside to the outside. UIt32(index) will convert our index which is of type Int to a type of UInt32, this is needed cause our random function requires a UInt32
Next up we request a random value which will lay between the index and 0 (thus between the bounds of the array). We want to remove the player who belongs to this random value in our array. To do this we need to convert our random Uint32 back to an Int we can do this by using Int().
Next we remove the player at the index using
playerNames.removeAtIndex(random)
Lastly we print the first (and only) item left in the array using
println(playerNames[0])

Resources