Nested array error [duplicate] - swift

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
}

Related

Swift, random word from let by pressing Button [duplicate]

As of Swift 1.2, Apple introduces Set collection type.
Say, I have a set like:
var set = Set<Int>(arrayLiteral: 1, 2, 3, 4, 5)
Now I want to get a random element out of it. Question is how? Set does not provide subscript(Int) like Array does. Instead it has subscript(SetIndex<T>). But firstly, SetIndex<T> does not have accessible initializers (hence, I can not just create an index with the offset I need), and secondly even if I can get the index for a first element in a set (var startIndex = set.startIndex) then the only way I can get to the N-th index is through consecutive calls to successor().
Therefore, I can see only 2 options at the moment, both ugly and expensive:
Convert the set into array (var array = [Int](set)) and use its subscript (which perfectly accepts Int); or
Get index of a first element in a set, traverse the chain of successor() methods to get to the N-th index, and then read corresponding element via set's subscript.
Do I miss some other way?
Starting with Swift 4.2, you can use randomElement:
let random = set.randomElement()
Probably the best approach is advance which walks successor for you:
func randomElementIndex<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
let i = advance(s.startIndex, n)
return s[i]
}
(EDIT: Heh; noticed you actually updated the question to include this answer before I added it to my answer... well, still a good idea and I learned something too. :D)
You can also walk the set rather than the indices (this was my first thought, but then I remembered advance).
func randomElement<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
for (i, e) in enumerate(s) {
if i == n { return e }
}
fatalError("The above loop must succeed")
}
In swift 3
extension Set {
public func randomObject() -> Element? {
let n = Int(arc4random_uniform(UInt32(self.count)))
let index = self.index(self.startIndex, offsetBy: n)
return self.count > 0 ? self[index] : nil
}
}
extension Set {
func randomElement() -> Element? {
return count == 0 ? nil : self[advance(self.startIndex, Int(arc4random()) % count)]
}
}
As per comments above re Swift updates, used a minor change for an extension to Set:
func randomElement() -> Element?
{
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
let index = startIndex.advancedBy(randomInt)
return count == 0 ? nil: self[index]
}
If you want a 'random' element from a Set then you use:
/// A member of the set, or `nil` if the set is empty.
var first: T? { get }
Get the 0th index or the 1,000,000th index makes no difference - they are all an arbitrary object.
But, if you want repeated calls to return a likely different element each time, then first might not fit the bill.

Why the fatal error: Array index out of range show when print array in Swift?

I am new in Swift. I create a node swift file to store the node information. And the other group swift file is a group which store the all node.
The code of Node.swift is like the following:
class Node {
var id:UInt8=0
var type:Int=0
var name:String=""
var bdAddr:NSUUID!
//The node private packet counter
var nodePktNum:Int=0
}
The code of Group.swift is like the following:
class Group {
var mLedDevice:[LedDevice]? = [LedDevice]()
class LedDevice {
var node :Node?
var rssi :Int?
}
func allocateNode()
{
print("mLedDevice![0].node = \(mLedDevice![0].node))")
}
}
When I try call function (allocateNode) and try to print mLedDevice![0].node) via print("mLedDevice![0].node = \(mLedDevice![0].node))")
It show the error fatal error: Array index out of range.
Did I missing something for initialize of var mLedDevice:[LedDevice]? = [LedDevice]() ?
Thanks in advance.
===================================EDIT=================================
I want to add the item into array , so I create a parameter like let let leddevice : LedDevice , and try to give it some value. And add the leddevice into array mLedDevice. But it show constant 'leddevice' used before being initialized.
How to give the init value for let leddevice : LedDevice ?
func allocateNode()
{
let leddevice : LedDevice
leddevice.node?.id = UInt8(0)
leddevice.node!.bdAddr = NodeUUID
mLedDevice?.append(leddevice)
}
The only thing I can think about that can cause this is that the array is empty i.e. you are attempting to access index 0 of that array but that doesn't exist.
Try the following and it may give you an insight on how to solve it after seeing the content of the array:
print("mLedDevice = \(mLedDevice))")
In other words you are instantiating an array with no elements in it.
In your line of code
var mLedDevice:[LedDevice]? = [LedDevice]()
You are only initializing an empty array. What you are trying afterwards is to access the first element, of an empty array, which is out of bounds.
Before your print statement, you will need to add an item to your array
var ledDevice = LedDevice()
mLedDevice.append(ledDevice)
And then your print statement would not give you any errors.
UPDATED: Answer for the added question
let leddevice : LedDevice is defining a constant of type LedDevice but is not yet initialized, and then it is being used in the next lines of code. You should replace it with
let leddevice = LedDevice()
Which will also initialize the variable.
Note: If you have any further questions, you should write a new question for that.
Note2: Have you read any guides about initialization?

Problems with generic Arrays in Swift [duplicate]

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.

Optional vs Bound value assigning var from array

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]
}

How to get random element from a set in Swift?

As of Swift 1.2, Apple introduces Set collection type.
Say, I have a set like:
var set = Set<Int>(arrayLiteral: 1, 2, 3, 4, 5)
Now I want to get a random element out of it. Question is how? Set does not provide subscript(Int) like Array does. Instead it has subscript(SetIndex<T>). But firstly, SetIndex<T> does not have accessible initializers (hence, I can not just create an index with the offset I need), and secondly even if I can get the index for a first element in a set (var startIndex = set.startIndex) then the only way I can get to the N-th index is through consecutive calls to successor().
Therefore, I can see only 2 options at the moment, both ugly and expensive:
Convert the set into array (var array = [Int](set)) and use its subscript (which perfectly accepts Int); or
Get index of a first element in a set, traverse the chain of successor() methods to get to the N-th index, and then read corresponding element via set's subscript.
Do I miss some other way?
Starting with Swift 4.2, you can use randomElement:
let random = set.randomElement()
Probably the best approach is advance which walks successor for you:
func randomElementIndex<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
let i = advance(s.startIndex, n)
return s[i]
}
(EDIT: Heh; noticed you actually updated the question to include this answer before I added it to my answer... well, still a good idea and I learned something too. :D)
You can also walk the set rather than the indices (this was my first thought, but then I remembered advance).
func randomElement<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
for (i, e) in enumerate(s) {
if i == n { return e }
}
fatalError("The above loop must succeed")
}
In swift 3
extension Set {
public func randomObject() -> Element? {
let n = Int(arc4random_uniform(UInt32(self.count)))
let index = self.index(self.startIndex, offsetBy: n)
return self.count > 0 ? self[index] : nil
}
}
extension Set {
func randomElement() -> Element? {
return count == 0 ? nil : self[advance(self.startIndex, Int(arc4random()) % count)]
}
}
As per comments above re Swift updates, used a minor change for an extension to Set:
func randomElement() -> Element?
{
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
let index = startIndex.advancedBy(randomInt)
return count == 0 ? nil: self[index]
}
If you want a 'random' element from a Set then you use:
/// A member of the set, or `nil` if the set is empty.
var first: T? { get }
Get the 0th index or the 1,000,000th index makes no difference - they are all an arbitrary object.
But, if you want repeated calls to return a likely different element each time, then first might not fit the bill.