Iterate through an optional array in Swift? - ios

I'm using the cocoapod SQLite for this project like so
import SQLite
var db = try! Connection()
var id: Expression<Int>!
var identifier: Expression<String>!
With it I am reading a list of moves from a SQLite database.
Every monster has a moves that they can learn. Some monsters can learn more moves than others.
var monster: Monster!
var monArray = [Monster]()
var dataSource: DataSource!
To get the monsters move ID I use this code. This allows me to grab the first move in the array. Changing the 0 would get me the second, third move ect.
monster.moves![0]["move_id"] as! Int
Now I'm using the SQLite database because I need to match monster ID values in my plist with the ones in the SQLite database. I use this code to do so
override func viewWillAppear(_ animated: Bool) {
let movesArray = Array(try! db.prepare(Table("moves").where(identifier == moves.name!.lowercased())))
for user in movesArray {
monArray = dataSource.mons.filter{ $0.moves![0]["move_id"] as! Int == user[id] }
}
}
Everything works fine until I try to increase the index range.
for user in movesArray {
for i in 0...6 {
monArray = dataSource.mons.filter{ $0.moves![i]["move_id"] as! Int == user[id] }
}
}
See where I replace the 0 with the range i? I do that because since monsters have more than one move, if I leave it at 0 my app will only display the monsters that learn that move as their first move. To better explain, my current code does not look through whether the monster knows the move, it only looks through whether the monsters knows the move as its first move.
In the above code I increase the range thinking it would solve my issue, but my app will crash because some monsters only have 1 move in their index, so anything above index 0 will crash with the error
fatal error: Index out of range
So to recap, I need to iterate through the entire array instead of just the first index, without it crashing. How can I achieve this?

Without all the story arround your just asking how to iterate over an array like here
for item in array as type {
...
}

Your question seems to be more of a logical question. If I'm understanding what you have said, then each monster will have a minimum of 1 move, but not guaranteed to have more. So you would need to account for this.
Having a fixed limit like you do will certainly cause problems if not all monsters have that many moves as the array will not always be that size.
I would do something like:
let monsterMoveCount = user.moves.count
for i in 0...monsterMoveCount
// Do whatever logic here
Hopefully this helps!

Related

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.

Storing and working with huge dictionaries in Swift

I am trying to load a dictionary with around 11,000 pairs into a Swift program (about .7MB). The values of the dictionary are arrays, and I need to be able to loop through the dictionary's arrays and compare those values with values in another array. There is an average of 10 items in the subarrays, although some have two or three, and some have hundreds.
The program crashes, and I am looking for a way to fix it while keeping the functionality from my Python-based prototype. I was thinking of using a bunch of property lists, and loading them into memory one by one, but this seems inefficient. What can I do to redesign this?
Some of the code which is being problematic:var dictOfHashesAndMaterials:[String:[Int]]: = ["10928123sdfb234w3fw3": [123,435,1,573,456,3234,653,57856,434,345],"13435fw323df0239dfsdf":[435,756,978,231,5668,485,9678,1233,87,989]] // and 11,000 additional entries which look similar to this.
And here's where I use it:` // function to populate the list of ingredients.
func populateListOfRecipes(){
var storedIngredients = UserDefaults.standard.object(forKey: "ingredients") as! [Int]
for(key, value) in dictOfHashesAndMaterials{
let listSet = Set(storedMaterials)
let findListSet = Set(value)
if findListSet.isSubset(of: listSet){
print("found a match!")
arrayOfAvailableProjects.append(key)
}
}
tableView.reloadData()
}`
A tableView is then populated by cells which link to the specifics of a given project. The details are filled in when the hashes are sent to the server, and the corresponding instructions are returned in full to the app.
Is it crashing in the simulator? I've worked with 30 GB files using the simulator without a crash. On the device is a different story...
Anyway, try this:
for(key, value) in dictOfHashesAndMaterials{
autoreleasepool {
let listSet = Set(storedMaterials)
let findListSet = Set(value)
if findListSet.isSubset(of: listSet){
print("found a match!")
arrayOfAvailableProjects.append(key)
}
}
}
tableView.reloadData()
}
By draining the auto release pool every iteration you will prevent running out of available memory.

How to temporarily remove swift array members?

I'm a bit new to Swift (so sorry if my question is a bit nooby) but...
So far my program takes strings in an array and displays them on the screen (Xcode)
I would like to implement a mechanism which makes it so that the user cannot get the same string twice in a row (when he/she presses the button).
My idea was to see if the random generated string is equal to the string already displayed on the label and (if thats true), delete the generated string from the array, run the function to display a random string from the array and then add that same string back after displaying a random fact.
Array of facts (in FactModel.swift):
struct FactModel {
var facts = [
"Ants stretch when they wake up in the morning.",
"Ostriches can run faster than horses.",
"Olympic gold medals are actually made mostly of silver.",
"etc..."
]
Function that gets returns a fact and the array index of that fact (In FactModel.swift):
func getRandomFact() -> (String,Int) {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(facts.count)
let checkFact = facts[randomNumber]
return (checkFact,randomNumber)
}
My ViewController code so far:
var mutableFactModel = FactModel()
#IBOutlet weak var FunFactButton: UIButton!
#IBOutlet weak var FunFact: UILabel!
#IBAction func ShowFunFact(sender: AnyObject) {
let localRandomFact = mutableFactModel.getRandomFact().0
if localRandomFact == FunFact.text {
mutableFactModel.facts.removeAtIndex(mutableFactModel.getRandomFact().1)
FunFact.text = mutableFactModel.getRandomFact().0
mutableFactModel.facts.append(mutableFactModel.getRandomFact().0)
} else {
FunFact.text = mutableFactModel.getRandomFact().0
}
}
It doesn't quite work the way I want it to, any ideas on making it work or a whole new way of going about it (not getting the same string twice in a row)?
Shuffle your facts array. Each time the user press the button, take the first element then remove it. Shuffling an array is akin to sort it in random order:
var facts = [
"Ants stretch when they wake up in the morning.",
"Ostriches can run faster than horses.",
"Olympic gold medals are actually made mostly of silver.",
"etc..."
].sort { f in arc4random_uniform(1000) % 2 == 0 }

Swift: Random number arrays inside images and variables with a for loop

I am creating a game in which, depending on the number of 'swipes' chosen to do, (let's say 3), 3 different patterns show on the screen, one by one. I am working on developing the first pattern.
So I have this:
if (swipes.no_of_swipes) == 3 {
swipeArray = Array<UInt32>(count: 3, repeatedValue: 0)
for i in 0 ..< 3 {
swipeArray[i] = arc4random_uniform(84)}
}
As far as I am aware, this code creates an array with three UInts which can be accessed by doing swipeArray[0], swipeArray[1], and swipeArray[2]. My first question is how long will this swipeArray stay the same? Until the close the view? Should I have a 'refresh button' when the user loses - and if so, how would I make one?
Then I have a property observer. You will notice the for loop, which I am using to keep code concise. I understand that I could do something like x++ somewhere in here so that it will go through each one.
var playBegin: Bool = false{
didSet {
if playBegin == true {
println("\(playBegin)")
var swipes = Menu()
if (swipes.no_of_swipes) == 3 {
for i in 0 ..< 3 {
patternRoom.image = UIImage(named: "pattern\(swipeArray[x])")
//rest of code
}
}
}
The pattern image comes from a set of 84 images named like pattern7 and pattern56. My second question is, how could I code the for loop to go through each swipeArray[x].
Thank you in advance,
Will
how long will this swipeArray stay the same?
This is a bit too open ended. It’ll stay the same until you assign a new value to it, either from this same bit of code or a different part. Only you can know when that will be, by looking at your code.
Since you express an interest in keeping the code concise, here’s a couple of code tips.
You might think about writing your first snippet’s loop like this:
swipeArray = (0..<swipes.no_of_swipes).map { _ in
arc4random_uniform(84)
}
This combines creating a new array and populating the values. By the way, just in case you don’t realize, there’s no guarantee this array won’t contain the same value twice.
It’s also probably better to make swipeArray of type [Int] rather than [UInt32], and to convert the result of arc4random to an Int straight away:
Int(arc4random_uniform(84))
Otherwise the UInt32s will probably be a pain to work with.
For your second for loop, you can do this:
for i in swipeArray {
patternRoom.image = UIImage(named: "pattern\(i)")
// rest of code
}
When writing Swift, usually (but not always), when you find yourself using array[x] there’s a better more expressive way of doing it.

Slow access to multidimensional array's items

I am experiencing one minor problem with Swift.
I am porting my old C++ code to it, and there are several places where I am using two-dimensional arrays.
Swift code now looks like:
var m_blocksSave:Array<Array<Int>>
var columns = 32
var rows = 32
//fill array with 0
for column in 0..<columns {
var columnArray = Array<Int>()
for row in 0..<rows {
columnArray.append(0)//default is 0
}
m_blocksSave.append(columnArray)
}
Then I want to check this array, find 1 for example. In fact any access to array's item creates a HUGE slowdown.
This code for example can take up to 4 seconds on my machine (regardless of environment, I can use iOS with simulator or run it as native OS X app):
for var i=0;i<columns;i++
{
for var j=0;j<rows;j++
{
let intVariable = m_blocksSave[i][j]//this thing is slow
}
}
What can I do in this situation and what is going on? Why is it so slow?
you can refer to : Most efficient way to access multi-dimensional arrays in Swift?.
In one word, 2D array is not so good right now, you need to construct your own 2D Array with 1D array.

Resources