How to prevent a 'Array index out of range' error? - swift

Is there a way, similar to using if let and/or optionals, to test whether you are about to index an empty buffer in Swift?

Define your own:
extension Array {
func ref (i:Int) -> T? {
return 0 <= i && i < count ? self[i] : nil
}
}
The ref() function returns an optional, so it can be nil, and you can use the if let syntax to access the returned value from ref() when it exists. You would use this as such:
var myA = [10,20,30]
if let val = myA.ref(index) {
// Use 'val' if index is < 3
}
else {
// Do this if the index is too high
}

Related

Is there a way to retrieve directly the value returned from a closure in Swift, with type the return type of the closure and not: () -> Type

This is a question that aims merely at elegance, but is there a way to make something to the following code work in Swift? I know the code does not work, what I want is that the result of the code within the closure is stored in a constant. And the underlying theoretical issue is whether or not it is possible to retrieve the returned value from the closure with type Int and not with type () -> Int.
Thanks a lot for any help or comment!
let tableWithBooleans: [Bool] = Array(repeating: false, count: 10)
tableWithBooleans[0] = true
tableWithBooleans[5] = true
let numberOfTrue: Int = {
var result: Int = 0
for i in 0...9 {
if tableWithBooleans[i] {
result += 1
}
}
return result
}
// I want the code to compile and numberOfTrue to be a constant equal to 2
Use a high-order function instead
let numberOfTrue = tableWithBooleans.reduce(0) { $1 ? $0 + 1 : $0 }
Now if you still want to use your closure code then you should add a () after the closing } since you are calling the code inside {} as a function
let numberOfTrue: Int = {
var result: Int = 0
for i in 0...9 {
if tableWithBooleans[i] {
result += 1
}
}
return result
}()

Swift - Unwrap optional in for in loop with where clause

I have a class with an optional member :
class A {
var i: Int? = nil
}
Then I have an array of objects of type A. Some objects in the array have a value for i, some others don't.
I want to iterate over objects in the array that have a value for i while unwrapping the optional at the same time. I didn't find a way to do both at the same time (I don't even know if it's possible), forcing me to write a if let construct inside the loop.
For example :
// a1, a2 have a value for i
let arr: [A] = [a1, a2, a3]
for obj in arr where obj.i != nil {
// I want to avoid if let, or force unwrapping here
if let unwrapped = obj.i {
print(i)
}
// let unwrapped = obj.i! ...
}
Is it possible in Swift ?
1.Maybe you can use flatMap to get value i, then print it
arr.flatMap{ $0.i }.forEach{ print($0) }
2.or Trying simple guard statement
arr.forEach { element in
guard let i = element.i else { return }
print(i)
}
I don't think that's possible.
Even if you have a where clause in your loop the type of obj is still of type A and as such i still remains optional.
To see why this is so think about the fact that you can change the value of i on object obj inside the loop, so the compiler is not sure that the value of i is valid until you unwrapp it.
You can try something like this
for obj in arr where obj.i != nil {
guard let i = obj.i else { continue }
print( i )
}
but if you start using guard you also skip the where clause
for obj in arr {
guard let i = obj.i else { continue }
print( i )
}
You can use case let syntax, but not without the help of map, and the result isn't the most readable:
for case let .some(unwrapped) in arr.map(\.i) {
print(unwrapped)
}
It's more useful if you're e.g. casting the outer object, e.g.:
for case let object as String in arrayOfAny {
if object.hasPrefix("tw") {
print("Starts with 'tw'")
}
}
instead of:
for object in arrayOfAny where object is String {
if object.hasPrefix("tw") { // Error: Value of type 'Any' has no member 'hasPrefix'
print("Starts with 'tw'")
}
}

(Swift) How would one check if a specific index of an array was empty?

var array:[Int] = []
var index = 5
if array[index] == nil {
array[index] = 1
}
Currently Xcode states that an int can not take the value of nil. So this code doesn't work. Is there any way to produce the same effect or verifying if a specific index is empty?
If the array can hold nil values, then its type must be an optional - as such:
var array:[Int?] = []
In general, when accessing an array based on an index you need to confirm that the index is in range. You might extend Array with
extension Array {
func ref (i:Index) -> Element? {
return i < count ? self[i] : nil
}
}
and then use as:
array.ref(i)
[Note: this will confound nil as 'out of range' with nil as 'value in optional array']
Declaring the property as:
var array:[Int] = []
the array property itself can be empty (no values) - as it is when you declare it as you did above, but it cannot contain nil values, so a check for nil shouldn't be necessary.
If you are checking to see if the array is empty, calling array.isEmpty, or checking array.count > 0 should do the trick.
If you need to store empty (non-initialized Int's/nil) in the array, then the array would be declared as:
var array:[Int?] = []
in which case the array could hold integers, or nil values. In this case you would need to safely unwrap the optional prior to using it, such as:
var array:[Int?] = [1, 2, nil, 4]
for item in array {
if let item = item {
print("item is \(item)")
} else {
print("item is nil")
}
}
// item is 1
// item is 2
// item is nil
// item is 4

Unwrapping optional inside of closure using reduce

I have a quick question that is confusing me a little bit. I made a simple average function that takes an array of optional Ints. I check to make sure the array does not contain a nil value but when I use reduce I have to force unwrap one of the two elements in the closure. Why is it that I only force unwrap the second one (in my case $1!)
func average2(array: [Int?]) -> Double? {
let N = Double(array.count)
guard N > 0 && !array.contains({$0 == nil}) else {
return nil
}
let sum = Double(array.reduce(0) {$0+$1!})
let average = sum / N
return average
}
I know it is simple but I would like to understand it properly.
The first parameter of reduce is the sum, which is 0 in the beginning. The second one is the current element of your array which is an optional Int and therefore has to be unwrapped.
Your invocation of reduce does this:
var sum = 0 // Your starting value (an Int)
for elem in array {
sum = sum + elem! // This is the $0 + $1!
}
EDIT: I couldn't get a more functional approach than this to work:
func average(array: [Int?]) -> Double? {
guard !array.isEmpty else { return nil }
let nonNilArray = array.flatMap{ $0 }
guard nonNilArray.count == array.count else { return nil }
return Double(nonNilArray.reduce(0, combine: +)) / Double(nonNilArray.count)
}
You can also discard the second guard if you want something like average([1, 2, nil]) to return 1.5 instead of nil

Nested Swift Dictionaries

I want to initialize a dictionary with a dictionary nested inside like this:
var a = [Int:[Int:Float]]()
a[1][2] = 12
But I get an error:
(Int:[Int:Float]) does not have a member named 'subscript'
I've hacked at a variety of other approaches, all of them running into some kind of issue.
Any idea why this doesn't work?
You can create your own 2D dictionary like this:
struct Dict2D<X:Hashable,Y:Hashable,V> {
var values = [X:[Y:V]]()
subscript (x:X, y:Y)->V? {
get { return values[x]?[y] }
set {
if values[x] == nil {
values[x] = [Y:V]()
}
values[x]![y] = newValue
}
}
}
var a = Dict2D<Int,Int,Float>()
a[1,2] = 12
println(a[1,2]) // Optional(12.0)
println(a[0,2]) // nil
The point is you access the element via a[x,y] instead of a[x][y] or a[x]?[y].
It's giving you that error because your first subscript returns an optional so it may return a dictionary or nil. In the case that it returns nil the second subscript would be invalid. You can force it to unwrap the optional value by using an exlamation point.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]![2]
If you aren't positive that a[1] is non-nil you may want to safely unwrap with a question mark instead.
var a = [1 : [ 2: 3.14]]
a[1]
a[1]?[2]
You can also assign using this method. (As of Beta 5)
var a = [Int:[Int:Float]]()
a[1] = [Int: Float]()
a[1]?[2] = 12.0
a[1]?[2] //12.0
Another way to do it is with an extension to the standard dictionary:
extension Dictionary {
mutating func updateValueForKey(key: Key, updater: ((previousValue: Value?) -> Value)) {
let previousValue = self[key]
self[key] = updater(previousValue: previousValue)
}
}
Example:
var a = [Int:[Int:Float]]()
a.updateValueForKey(1) { nestedDict in
var nestedDict = nestedDict ?? [Int:Float]()
nestedDict[2] = 12
return nestedDict
}