Addressing a multi-dimensional array of objects in Swift - arrays

Extreme newbie Swift syntax question...
Trying to address a multidimensional array of UIImageView objects and getting the error
Cannot subscript a value of type [[UIImageView]] with an index of
type UInt32.
I thought I was creating an NxN array of UIImageView objects but I guess not?
Thanks All!
func loadDefaultImages()
{
var pictures : [[UIImageView]] = [];
let MaxRows: UInt32 = 4
let MaxCols: UInt32 = 4
var row: UInt32
var col: UInt32
for (row = 0; row <= MaxRows; row++)
{
for (col = 0; col <= MaxCols; col++)
{
var newImage = UIImageView(image: UIImage(named: "first"))
pictures[row][col] = UIImageView() /* Error on this statement */
}
}
}

Initially, your pictures array will not contain any elements, (i.e. it will evaluate to []). As you try to set pictures[0][0], you're trying to reference both a row and column that do not exist yet.
Instead, you could build up the array as you go, like this:
func loadDefaultImages() {
var pictures : [[UIImageView]] = [];
let MaxRows: UInt32 = 4
let MaxCols: UInt32 = 4
for _ in 0..<MaxRows {
var aRow : [UIImageView] = []
for _ in 0..<MaxCols {
let newImage = UIImageView(image: UIImage(named: "first"))
aRow.append(newImage)
}
pictures.append(aRow)
}
}
However, a neater way to do this would be to create the array with a default size using the count, repeatedValue function, like so:
func loadDefaultImages() {
let MaxRows: Int = 4
let MaxCols: Int = 4
var pictures = [[UIImageView?]](count: MaxRows, repeatedValue: [UIImageView?](count: MaxCols, repeatedValue: nil))
for row in 0..<MaxRows {
for col in 0..<MaxCols {
let newImage = UIImageView(image: UIImage(named: "first"))
pictures[row][col] = newImage
}
}
}

Related

How to use two-dimensions array in Swift?

So I have this array var arrayItinerario: Array<Any> = [] which is intended to hold arrays of type Itinerario, var temporalArray: Array<Itinerario> = []
Next, this arrayItinerario would later be used to access properties from the struct Itinerario from where the array temporalArray type comes from.
cell.siteLabel.text = "\([arrayItinerario[indexPath.section]][indexPath.row].ubicacion)"
The problem is that arrayItinerario is not an Itinerario type object which make it impossible to access ubicacion to make an example
I have tried
let object = [arrayItinerario[indexPath.section]][indexPath.row] as! Itinerario
But the cast throws an error. How can I do to access properties from the arrays temporalArraythat are inside arrayItinerario?
Note: I am using indexPath because I am filling table cells
var arrayItinerario: [[Itinerario]] = []
//fill data
var temporalArray: [Itinerario] = []
arrayItinerario.append(temporalArray)
cell.siteLabel.text = "\(arrayItinerario[indexPath.section][indexPath.row].ubicacion)"
or
var arrayItinerario: Array<Any> = []
var temporalArray: Array<Itinerario> = []
guard let temp = arrayItinerario[indexPath.section] as? Array<Itinerario>{
return
}
cell.siteLabel.text = "\(temp[indexPath.row].ubicacion)"
Here's an implementation I've used in a few projects:
import Foundation
class Array2D<T> {
var cols:Int, rows:Int
var matrix:[T?]
init(cols: Int, rows: Int, defaultValue:T?) {
self.cols = cols
self.rows = rows
matrix = Array(repeating: defaultValue, count: cols*rows)
}
subscript(col:Int, row:Int) -> T? {
get{
return matrix[cols * row + col]
}
set{
matrix[cols * row + col] = newValue
}
}
func colCount() -> Int { self.cols }
func rowCount() -> Int { self.rows }
}
Because it's generic you can store whatever type you like in it. Example for Ints:
// Create 2D array of Ints, all set to nil initially
let arr = Array2D<Int>(cols: 10, rows: 10, defaultValue: nil)

Increment value in an array of dictionaries

I'm trying to calculate the number of times a specific number is rolled in a set of six six-sided dice, to determine wether or not I have three of a kind, four of a kind, etc.
I can pull the face value of each die rolled and compare it to the faces on a 6 sided die but can't get the "qtyRolled" key/value to increment.
func rollDice() {
currentRoll.removeAll()
for _ in currentDiceArray {
let num: UInt32 = arc4random_uniform(UInt32(currentDieFaceArray.count))
let currentDieData = currentDieFaceArray[Int(num)]
let faceValue = currentDieData["faceValue"]
currentRoll.append(faceValue as! Int)
}
print(currentRoll)
getQtyOfDieFaces()
//checkForScoringCombos()
}
func getQtyOfDieFaces() {
for die in currentRoll {
for dieData in currentDieFaceArray {
var currentDieData = dieData
let qtyRolled = currentDieData["qtyRolled"] as! Int
let faceValue = currentDieData["faceValue"] as! Int
print("faceValue: \(faceValue)")
print("Die: \(die)")
if faceValue == die {
currentDieData["qtyRolled"] = qtyRolled + 1 as AnyObject
}
}
}
for currentDieData in currentDieFaceArray {
print(currentDieData["qtyRolled"]!)
}
}
Here are my data structures
var currentDieFaceArray = [[String:AnyObject]]()
var currentDiceArray:[[String:AnyObject]] = [[:]]
var currentRoll: [Int] = []
I'd recommend ditching the dictionaries unless you really need them, as you're really just dealing with properties of a struct/class. I'm going to assume you're using the currentDieFaceArray method so that you can make this generic for non-linear dice faces of other dimensions (e.g. you can have a four-sided dice with the face values [1, 4, 6, 8]). If this isn't the case, you can simplify further I'm sure with a simple array of counts. But here's an example with your method (probably has other possible optimisations).
class DieFaceDefn
{
let faceValue : Int
var countThisRoll : Int = 0
init(faceValue: Int)
{
self.faceValue = faceValue
}
}
var diceFaces: [DieFaceDefn] = []
let numberOfCurrentDice = 5
func setupDice()
{
diceFaces.append(DieFaceDefn(faceValue: 1))
diceFaces.append(DieFaceDefn(faceValue: 2))
...
}
var currentRoll: [Int] = []
func rollDice()
{
currentRoll.removeAll()
diceFaces.forEach { $0.countThisRoll = 0 }
for _ in 0..<numberOfCurrentDice
{
let num: UInt32 = arc4random_uniform(UInt32(diceFaces.count))
let currentDieData = diceFaces[Int(num)]
let faceValue = currentDieData.faceValue
currentRoll.append(faceValue)
currentDieData.countThisRoll += 1
}
print(currentRoll)
diceFaces.forEach { print("\($0.faceValue): \($0.countThisRoll)") }
}

Swift: Index Out of Range Error when Populating Two-Dimensional Array

I just started to learn Swift and I would like to code the game BubbleBreaker which I have already created in both Java and C# some years ago.
For this, I wanted to create a two-dimensional array of Bubble (which is derived from SKSpriteNode), however, when I am trying to populate the array, I always get an "index out of range error" at index [0][0]. Can somebody please help me?
class GameScene: SKScene {
//game settings
private var columns = 10
private var rows = 16
private var bubbleWidth = 0
private var bubbleHeight = 0
//bubble array
private var bubbles = [[Bubble]]()
override func didMove(to view: SKView) {
initializeGame()
}
private func initializeGame() {
self.anchorPoint = CGPoint(x: 0, y: 0)
//Optimize bubble size
bubbleWidth = Int(self.frame.width) / columns
bubbleHeight = Int(self.frame.height) / rows
if bubbleWidth < bubbleHeight {
bubbleHeight = bubbleWidth
}
else {
bubbleWidth = bubbleHeight
}
//Initialize bubble array
for i in 0 ... columns-1 {
for j in 0 ... rows-1 {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
bubbles[i][j] = newBubble // THIS IS WERE THE CODE BREAKS AT INDEX [0][0]
self.addChild(newBubble)
}
}
}
}
bubbles starts off empty. There's nothing at any index.
Update your loop to something like this:
//Initialize bubble array
for i in 0 ..< columns {
var innerArray = [Bubble]()
for j in 0 ..< rows {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
innertArray.append(newBubble)
self.addChild(newBubble)
}
bubbles.append(innerArray)
}
This builds up an array of arrays.
Instead of assigning new value as unexisting value, append new empty array of Bubble for every column and then append to this array newBubble for every row
for i in 0 ... columns-1 {
bubbles.append([Bubble]())
for j in 0 ... rows-1 {
let size = CGSize(width: bubbleWidth, height: bubbleHeight)
let newBubble = Bubble(size: size)
newBubble.position = CGPoint(x: i*bubbleWidth, y: j*bubbleHeight)
bubbles[i].append(newBubble)
self.addChild(newBubble)
}
}

Swift Button Array

In a Swift + SQLite project I'm trying to populate an array of 4 buttons in my UI with a SQL query. The best I could do so far is as the code below shows, but I'm sure it can be done in a cleaner way. I tried to do both the SQL query result reading and button change in the same loop but got all sorts of type mismatch and reference errors.
Any suggestion will be much appreciated.
var answerText: Array<String?> = ["","","",""]
class ViewController: UIViewController {
#IBOutlet var answerButton: Array<UIButton> = []
func displayQuestion(questionNumber: Int){
let querySQL = "SELECT answer FROM answers WHERE answers.question = '\(questionNumber)'"
let results:FMResultSet? = myDatabase.executeQuery(querySQL,
withArgumentsInArray: nil)
var j=0
while results?.next() == true {
answerText[j] = results?.stringForColumn("answer")
j=j+1
}
j=0
for item in answerButton{
var button:UIButton = item as UIButton
button.setTitle(answerText[j], forState: UIControlState.Normal)
j=j+1
}
}
}
you are getting reference error because you are declaring an array as a IBoutlet . if your answerButton having objects of UIButton than that should not be #IBOutlet var answerButton: Array = [].
answerButton declared like this var answerButton: Array = [].
please modify below code according to you code if you want to test the below code than just copy and past it into func viewDidLoad() of your viewcontroller and this will show you four buttons on you view
var answerText: Array<String?> = ["Button1","Button2","Button3","Button4"]
var answerButton: Array<UIButton> = []
for var i = 0; i<=answerText.count; i++ {
var button = UIButton.buttonWithType(.System) as! UIButton
button.titleLabel!.font = UIFont.systemFontOfSize(12)
button.titleLabel!.lineBreakMode = .ByTruncatingTail
var numberOfButton = 5
let j = i
var y:CGFloat = (CGFloat)(j*690/(numberOfButton+1)-21);
button.frame.size.height = 42
button.frame.size.width = 150
button.frame.origin.y = y
button.frame.origin.x = 250.0
answerButton .insert(button, atIndex: i)
}
for var i = 0; i<answerText.count; i++ {
var button:UIButton = answerButton [i]
button.setTitle(answerText[i], forState: UIControlState.Normal)
self.view .addSubview(button)
}
}
var buttons: [UIButton?] {
return [self.firstButton, self.secondButton, self.thirdButton]
}

Iterate over two arrays simultaneously

I am new to Swift. I have been doing Java programming. I have a scenario to code for in Swift.
The following code is in Java. I need to code in Swift for the following scenario
// With String array - strArr1
String strArr1[] = {"Some1","Some2"}
String strArr2[] = {"Somethingelse1","Somethingelse2"}
for( int i=0;i< strArr1.length;i++){
System.out.println(strArr1[i] + " - "+ strArr2[i]);
}
I have a couple of arrays in swift
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for data in strArr1{
println(data)
}
for data in strArr2{
println(data)
}
// I need to loop over in single for loop based on index.
Could you please provide your help on the syntaxes for looping over based on index
You can use zip(), which creates
a sequence of pairs from the two given sequences:
let strArr1 = ["Some1", "Some2"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
for (e1, e2) in zip(strArr1, strArr2) {
print("\(e1) - \(e2)")
}
The sequence enumerates only the "common elements" of the given sequences/arrays. If they have different length then the additional
elements of the longer array/sequence are simply ignored.
With Swift 5, you can use one of the 4 following Playground codes in order to solve your problem.
#1. Using zip(_:_:) function
In the simplest case, you can use zip(_:_:) to create a new sequence of pairs (tuple) of the elements of your initial arrays.
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let sequence = zip(strArr1, strArr2)
for (el1, el2) in sequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#2. Using Array's makeIterator() method and a while loop
It is also easy to loop over two arrays simultaneously with a simple while loop and iterators:
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
var iter1 = strArr1.makeIterator()
var iter2 = strArr2.makeIterator()
while let el1 = iter1.next(), let el2 = iter2.next() {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#3. Using a custom type that conforms to IteratorProtocol
In some circumstances, you may want to create you own type that pairs the elements of your initials arrays. This is possible by making your type conform to IteratorProtocol. Note that by making your type also conform to Sequence protocol, you can use instances of it directly in a for loop:
struct TupleIterator: Sequence, IteratorProtocol {
private var firstIterator: IndexingIterator<[String]>
private var secondIterator: IndexingIterator<[String]>
init(firstArray: [String], secondArray: [String]) {
self.firstIterator = firstArray.makeIterator()
self.secondIterator = secondArray.makeIterator()
}
mutating func next() -> (String, String)? {
guard let el1 = firstIterator.next(), let el2 = secondIterator.next() else { return nil }
return (el1, el2)
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let tupleSequence = TupleIterator(firstArray: strArr1, secondArray: strArr2)
for (el1, el2) in tupleSequence {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
#4. Using AnyIterator
As an alternative to the previous example, you can use AnyIterator. The following code shows a possible implementation of it inside an Array extension method:
extension Array {
func pairWithElements(of array: Array) -> AnyIterator<(Element, Element)> {
var iter1 = self.makeIterator()
var iter2 = array.makeIterator()
return AnyIterator({
guard let el1 = iter1.next(), let el2 = iter2.next() else { return nil }
return (el1, el2)
})
}
}
let strArr1 = ["Some1", "Some2", "Some3"]
let strArr2 = ["Somethingelse1", "Somethingelse2"]
let iterator = strArr1.pairWithElements(of: strArr2)
for (el1, el2) in iterator {
print("\(el1) - \(el2)")
}
/*
prints:
Some1 - Somethingelse1
Some2 - Somethingelse2
*/
Try This:
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0,$1)
}
zip([0,2,4,6], [1,3,5,7]).forEach {
print($0.0,$0.1)
}
You could also enumerate over one array and used the index to look inside the second array:
Swift 1.2:
for (index, element) in enumerate(strArr1) {
println(element)
println(strArr2[index])
}
Swift 2:
for (index, element) in strArr1.enumerate() {
print(element)
print(strArr2[index])
}
Swift 3:
for (index, element) in strArr1.enumerated() {
print(element)
print(strArr2[index])
}
You could use Range if you still want to use for in.
var strArr1: [String] = ["Some1","Some2"]
var strArr2: [String] = ["Somethingelse1","Somethingelse2"]
for i in Range(start: 0, end: strArr1.count) {
println(strArr1[i] + " - " + strArr2[i])
}
for(var i = 0; i < strArr1.count ; i++)
{
println(strArr1[i] + strArr2[i])
}
That should do it. Never used swift before so make sure to test.
Updated to recent Swift syntax
for i in 0..< strArr1.count {
print(strArr1[i] + strArr2[i])
}
> Incase of unequal count
let array1 = ["some1","some2"]
let array2 = ["some1","some2","some3"]
var iterated = array1.makeIterator()
let finalArray = array2.map({
let itemValue = iterated.next()
return "\($0)\(itemValue != nil ? "-"+itemValue! : EmptyString)" })
// result : ["some1-some1","some2-some2","some3"]

Resources