This question already has an answer here:
Swift: How to declare a 2d array (grid or matrix) in Swift to allow random insert
(1 answer)
Closed 7 years ago.
Since I'm pretty new to native programming in iOS, I need some help with the following situation. What I need is a class which holds an array or dictionary with a combination of 2 integers as the key (used for coordiates in a grid matrix) and an Integer as the value. I found a solution in a tutorial somewhere, which looks like this:
class Array2D<T> {
let columns: Int
let rows: Int
var array: Array<T?>
init(columns: Int, rows: Int) {
self.columns = columns
self.rows = rows
array = Array<T?>(count: rows * columns, repeatedValue: nil)
}
subscript(column: Int, row: Int) ->T! {
get {
return array[(row * columns) + column]
}
set(newValue) {
array[(row * columns) + column] = newValue
}
}
}
Unfortunately, this type of array already has the length of rows*columns, as soon as the class is initialized, which I don't want. The array should be empty in the beginning. But if I initialize an empty Array, I get problems when it comes to getting or setting the value for a key which doesn't exist yet. Also, I need to be able to easily increment the values of a specific key (like array[2, 3]++). Does anybody have a good solution for my problem?
Just use it with optionals so there are no initial values set. In this example you have your columns an your rows which are optionals and initialized with nil. The array is initialized as an empty array.
class Array2D<T> {
let columns: Int? = nil
let rows: Int? = nil
var array: Array<T?> = []
}
Why not create struct for a coordinate like
struct Coordinate {
let column: Int
let row: Int
}
... then provide necessary implementations for Equatable and Hashable protocols, and then use it as a key in dictionary like:
struct Array2D<T> {
private var storage = [Coordinate: T]()
// ...
}
... and then provide subscript for that Array2D, which when a value is assigned would simply insert new entry into an underwater dictionary, and when the value is read, just read it from there.
Related
I want to initialize every time a struct with dictionaries. Later, I'm going to use its properties instead a dictionary's keys and values - it seems rather easier. However, when I try the code below, it tells me that "Return from initializer without initializing all stored properties" and "1. 'self.one' not initialized" and "2. 'self.two' not initialized". My question is how to initialize a struct from a dictionary, so that I have basically a struct with the contents of the dictionary? Or how to transform it into struct?
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
one = ""
two = []
for i in three {
self.one = i.key
self.two = i.value
}
} ERROR! - Return from initializer without initializing all stored properties
}
for in clause may have zero runs, in which case struct properties will not be initialized. You have to provide default values (or emit fatalError if you really need to).
While I think your example is pure synthetical, there is no need to loop through array, you can set properties to its last entry.
The issues is that if three is an empty Dictionary, the instance properties one and two don't get initialised. Also, you are overwriting the properties in each iteration of the for loop and the compiler cannot guarantee that there will be any iterations of the loop in compile-time, hence the compiler error.
You could make the initialiser failable to account for this by checking that the dictionary actually contains at least one key-value pair and assigning that first key-value pair to your properties.
struct Blabla {
var one: String
var two: [Int]
init?(three: [String: [Int]]) {
guard let key = three.keys.first, let value = three[key] else { return nil }
one = key
two = value
}
}
However, you should rethink what it is that you are actually trying to achieve, since with your current setup you have a mismatch between your init input values and the properties of your struct.
This code should compile, but it feels unsafe to me to initialize a Struct in this way because:
It assume your dictionary has values in it.
Your stored properties will always have the last value you looped through.
In order to pull values out to satisfy the compiler you need to force unwrap them. (With Dávid Pásztor's guard-letting approach, this can be avoided)
struct Blabla {
var one: String
var two: [Int]
init(three: [String: [Int]]) {
self.one = three.keys.first!
self.two = three[three.keys.first!]!
}
}
let input = ["pizza": [1,2]]
let l = Blabla(three: input)
If I were you I would let the memberwise initializer that you get for free do its thing and provide either a specialized initializer to handle your case of taking a Dictionary as input or move that parsing to another function/class/etc....
The compiler error is clear: If the dictionary is empty the struct members are never initialized. But the code makes no sense anyway as each iteration of the dictionary overwrites the values.
Maybe you mean to map the dictionary to an array of the struct
struct Blabla {
let one: String
let two: [Int]
}
let three = ["A":[1,2], "B":[3,4]]
let blabla = three.map{Blabla(one: $0.key, two: $0.value)}
print(blabla) // [Blabla(one: "A", two: [1, 2]), Blabla(one: "B", two: [3, 4])]
struct blabla{
var a : string
var b : [int] = []
init(_ data: [string:[int]]){
// whatever you want to do
}
}
Consider the following code:
struct Card {
var name0: String
var name1: String
}
var cards = [Card]()
cards.append(Card(name0: "hello", name1: "world"))
// Need to perform array index access,
// every time I want to mutate a struct property :(
cards[0].name0 = "good"
cards[0].name1 = "bye"
// ...
// ...
// "good bye"
print(cards[0].name0 + " " + cards[0].name1)
Instead of having to perform multiple array index accessing every time I want to mutate a property in struct, is there a technique to avoid such repeating array index accessing operation?
// Ok. This is an invalid Swift statement.
var referenceToCardStruct = &(cards[0])
referenceToCardStruct.name0 = "good"
referenceToCardStruct.name1 = "bye"
// ...
// ...
There are a lot of good answers here, and you should not think of value types as "a limitation." The behavior of value types is very intentional and is an important feature. Generally, I'd recommend inout for this problem, like matt suggests.
But it is also certainly possible to get the syntax you're describing. You just need a computed variable (which can be a local variable).
let index = 0 // Just to show it can be externally configurable
var referenceToCardStruct: Card {
get { cards[index] }
set { cards[index] = newValue }
}
referenceToCardStruct.name0 = "good"
referenceToCardStruct.name1 = "bye"
print(cards[0].name0 + " " + cards[0].name1)
struct Card {
var name0: String
var name1: String
}
var cards = [Card]()
// every time I want to mutate a struct property :(
cards[0].name0 = "good"
cards[0].name1 = "bye"
Instead of having to perform multiple array index accessing every time I want to mutate a property in struct, is there a technique to avoid such repeating array index accessing operation?
No. When you have an array of struct, then in order to make a change to a struct within the array, you must refer to that struct by index.
If you don't want to see the repeated use of the index, you can hide it in a function using inout:
func mutate(card: inout Card) {
card.name0 = "good"
card.name1 = "bye"
}
for index in cards.indices {
mutate(card:&cards[index])
}
Some day, Swift may include for inout which will allow you to cycle through an array of struct and mutate each struct instance directly. But that day is not yet here.
In answer to the implied question whether it is worth switching to a class just to avoid this repeated use of the index, my answer would be No. There is a good reason for using structs — they are much easier to reason about than classes, and are one of Swift's best features — and I would keep using them if that reason matters to you.
struct is a value type you can't get a reference to it's object with assignment , you should go that way , use a mutating method like https://stackoverflow.com/a/52497495/5820010 or use a class instead
If you don't want to repeat the index, then create a variable from the value you want.
var cards = [Card]()
cards.append(Card(name0: "hello", name1: "world"))
var card = cards[0]
card.name0 = "good"
card.name1 = "bye"
// ...
// ...
cards[0] = card // update the array with the updated card
// "good bye"
print(card.name0 + " " + card.name1)
I think the mutating method is the way to go, as Sh_Khan points out.
In your case, I would do something like:
1> struct Card {
2. var name0: String
3. var name1: String
4. }
5.
6. var cards = [Card]()
7. cards.append(Card(name0: "hello", name1: "world"))
cards: [Card] = 1 value {
[0] = {
name0 = "hello"
name1 = "world"
}
}
8> extension Card {
9. mutating func setNames(name0: String, name1: String) {
10. self.name0 = name0
11. self.name1 = name1
12. }
13. }
14> cards[0].setNames(name0: "x", name1: "y")
15> cards
$R0: [Card] = 1 value {
[0] = {
name0 = "x"
name1 = "y"
}
}
Another approach is a wholesale update of cards[0].
Kind of makes you wish for record updating syntax (a la Haskell or Elm) or dictionary merging-type functionality. But look on the bright side. Maybe Swift's lack of making this easy is testament to the fact that it has static-typing-and-value-semantics-while-allowing-mutation and that combination of features, I think, makes the mutating func or full array element update all but required. I'm not sure Swift has a syntactic feature for updating multiple properties in one shot (without writing your own function or method).
This question already has answers here:
Arrays and Swift
(3 answers)
Closed 5 years ago.
I know this is a really newbie question but it has thrown me for days and I can't seem to find a solution that I actually understand.
I am trying to make a nested array to store latitude and longitude, however it Xcode/playground throws a EXC_BAD_INSTRUCTION error.
I want to declare, initialise and print the contents of an array. What am I doing wrong?
var arrayLocations:[[Float]] = []
arrayLocations[0] = [27.1750199, 78.0399665]
print("\(arrayLocations[0][0]) and \(arrayLocations[0][1])")
You cannot assign a value to index 0 because there is no index 0 if the array is empty.
You have to append or insert the item:
arrayLocations.append([27.1750199, 78.0399665])
You cannot assign value into 0 position because index 0 is empty.
You can do this in following ways -
With initial value -
var arrayLocations:[[Float]] = [[0.00,0.00]]
arrayLocations[0] = [27.1750199, 78.0399665]
print("\(arrayLocations[0][0]) and \(arrayLocations[0][1])")
Otherwise you can do this using append or insert as vadian's answer.
As vadian said on his answer
You cannot assign a value to index 0 because there is no index 0 if
the array is empty.
You have to append or insert the item:
arrayLocations.append([27.1750199, 78.0399665])
I suggest that you make use of this Collection extension so that your code won't crash often
extension Collection where Indices.Iterator.Element == Index {
/// Returns the element at the specified index if it is within bounds, otherwise nil.
subscript (safe index: Index) -> Generator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
By using this you could just use if let blocks
Sample:
if let aLocation = arrayLocations[safe: 0] {
aLocation = [27.1750199, 78.0399665]
}
This ensures that even if you try to access an object of index 0 your code won't crash.
Suggestion:
Note: This suggestion is not part of the answer, but rather something to improve your code.
It looks like you're trying to create an array of latitude and longitude. Using an array for the latitude and longitude object isn't very wise. I suggest that you create an object instead, it can be a struct or a typeAlias
For example:
struct CoordinateStruct {
var longitude: Float
var latitude: Float
}
/* or */
typealias CoordinateTypeAlias = (latitude: Float, longitude: Float)
/************************* */
// Your code would then look like this
var arrayLocations:[CoordinateStruct] = []
arrayLocations.append(CoordinateStruct(longitude: 27.12312312, latitude: 78.123123))
/* or */
var arrayLocations:[CoordinateTypeAlias] = []
arrayLocations.append((27.12312312, 78.123123))
// Which would make accessing them very easy and readable, like this.
if let aLocation = arrayLocations[safe: 0] {
print(aLocation.longitude)
print(aLocation.latitude)
// instead of this
print(aLocation[0]) // a person who reads your code won't understand this
print(aLocation[1]) // a person who reads your code won't understand this
}
I am quite a confused when and how to declare variables in particular points in Swift and its causing a headache for a new guy like me in SWIFT.
What is the difference between the following type of declarations? I have given my thoughts and understanding on them. Please rectify me with your solution if I am wrong and be a bit explanatory so that I can know the actual and exact answer.
Array -
1) var arr = NSArray()//I think its an instance of immutable NSArray type
2) var arr = NSMutableArray()
3) var arr = Array()//I have no idea of difference between NSArray and Array type. Might be both are same
4) var arr : NSMutableArray?//Creates an optional type but how is it different from line no.2
5) var arr : NSMutableArray = []//creates an empty array NSMutableArray type and again how is it different from line no.2 & 3
Please clarify a bit clearly so that my confusion level would be a bit clear. Thanks
Array is a swift type where as NSArray is an objective C type. NS classes support dynamic-dispatch and technically are slightly slower to access than pure swift classes.
1) var arr = NSArray()
arr is an NSArray() here - you can re-assign things to arr but you can't change the contents of the NSArray() - this is a bad choice to use IMO because you've put an unusable array into the variable. I really can't think of a reason you would want to make this call.
2) var arr = NSMutableArray()
Here you have something usable. because the array is mutable you can add and remove items from it
3) var arr = Array()
This won't compile - but var arr = Array<Int>() will.
Array takes a generic element type ( as seen below)
public struct Array<Element> : CollectionType, MutableCollectionType, _DestructorSafeContainer {
/// Always zero, which is the index of the first element when non-empty.
public var startIndex: Int { get }
/// A "past-the-end" element index; the successor of the last valid
/// subscript argument.
public var endIndex: Int { get }
public subscript (index: Int) -> Element
public subscript (subRange: Range<Int>) -> ArraySlice<Element>
}
4) var arr : NSMutableArray?
You are defining an optional array here. This means that arr starts out with a value of nil and you an assign an array to it if you want later - or just keep it as nil. The advantage here is that in your class/struct you won't actually have to set a value for arr in your initializer
5) var arr : NSMutableArray = []
It sounds like you are hung up on confusion about Optional values.
Optional means it could be nil or it could not
When you type something as type? that means it is nil unless you assign it something, and as such you have to unwrap it to access the values and work with it.
#G.Abhisek at first about you question. var arr: NSMutableArray = [] and var arr = NSMutableArray() means the same. the first one means, i ask the compiler to create a variable of type NSMutableArray and initialize it as an empty NSMutableArray. the second one means, i ask the compiler to create a variable and assign to it an empty initialized NSMutableArray. in the second case the compiler has to infer the right type of the variable, in the first case i did it by myself. still, the result will be the same. var arr1: Array<AnyObject> = [] and var arr2: NSMutableArray = [] are totally different things!. arr1 srores value type Array, arr2 stores reference to the instance of an empty NSMutableArray class. you can write let arr2: NSMutableArray = [] and next you can add an object there ... but you are not able to do thinks like arr2 = ["a","b"]. arr2 is constant, not variable, so the value stored there is imutable.
i am again close to my computer ... in the code below, you can see the main differences between swift and foundation arrays
import Foundation
let arr1: NSMutableArray = []
arr1.addObject("a")
arr1.addObject(10)
arr1.forEach {
print($0, $0.dynamicType)
/*
a _NSContiguousString
10 __NSCFNumber
*/
}
var arr2: Array<Any> = []
arr2.append("a")
arr2.append(10)
arr2.forEach {
print($0, $0.dynamicType)
/*
a String
10 Int
*/
}
var arr3: Array<AnyObject> = []
arr3.append("a")
arr3.append(10)
arr3.forEach {
print($0, $0.dynamicType)
/*
a _NSContiguousString
10 __NSCFNumber
*/
}
print(arr1.dynamicType, arr2.dynamicType, arr3.dynamicType)
// __NSArrayM Array<protocol<>> Array<AnyObject>
I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}