I want to create an array, let's say called nums and I want this array to hold either a Double or an Error value, and nothing else. Currently, I can get around the problem by instantiating nums as follows:
var nums = Array<(Double?, Error?)>()
Then I go through and do something like:
nums.append((5.0, nil))
nums.append((nil, Error.invalidNumber))
nums.append((10.0, nil))
This works, but instead of having nums have either a Double or an Error like I want, it has a tuple of those values. How can I change the instantiation of the array so that I only need to append one of the values to the array?
My goal is to be able to do this:
nums.append(5.0)
nums.append(Error.invalidNumber)
nums.append(10.0)
Result if the type for your needs. It is a generic enum whose value can either be success(Value) (where Value is Double for your case) or failure(Error).
var nums = [Result<Double, Error>]()
And then you can do:
nums.append(.success(5.0))
nums.append(.failure(Error.invalidNumber))
nums.append(.success(10.0))
What you want is not a tuple but an enum:
enum DoubleOrNothing {
case double(Double)
case nothing(Error)
}
Now it can only be one or the other and the "value" is the associated value of the case that it is. Just make an array of that enum and you're home free.
enum MyError : Error {
case invalidNumber
}
var nums = Array<DoubleOrNothing>()
nums.append(.double(5.0))
nums.append(.nothing(MyError.invalidNumber))
nums.append(.double(10.0))
David's answer is what to go with if your errors are all the same type. Otherwise, you need to use throwing get accessors. (A [() throws -> Double].)
var getDoubleArray = [
{ 5.0 },
{ throw Error.invalidNumber },
{ 10 }
]
Related
I have an array of object, each object contains a discount rate , I need to sort them increasingly by their rate,
struct ShopDetails {
var shopId: Int?
var discountRate: String?
init(with json: Dictionary<String,Any>) {
shopId = json["id"] as? Int
discountRate = json["discount"] as? String
}
I tired to sort them using this method;
func getShopsByDiscount() {
let sortedImages = self.copyOfShops.sorted(by: { (shop1: ShopDetails, shop2: ShopDetails) -> Bool in
return Int(shop1.discountRate) < Int(shop2.discountRate)
})
}
I tried to cast the rate to integer, since its been received form the backend as string, but I got an error:
value of type Any has no member discountRate.
any idea how to do it? if there is a way to do it without casting it will be better
First, you need to verify that the array you're starting with is of type [ShopDetails]. The error indicates that this is probably an Objective-C NSArray, which won't work as well in Swift. If you're unclear about this, I suggest you Google the topic: there's no reason to use NSArray in Swift.
Below, I assume the array is the correct type ([ShopDetails]). From here, you need to do two additional things, because discountRate is of type String?.
You need to check if the string is actually there
You need to check if it can actually be expressed as an Int.
With these things in mind, your sort function can look like this:
let sortedImages = copyOfShops.sorted(by: {
(shop1: ShopDetails, shop2: ShopDetails) -> Bool in
if let shop1String = shop1.discountRate, let shop1Value = Int(shop1String),
let shop2String = shop2.discountRate, let shop2Value = Int(shop2String) {
return shop1Value < shop2Value
}
return true
})
That being said, the best way to handle this is to change the type of discountRate from String? to Int, and do the above checks when you init(with json: Dictionary<String,Any>). If the server giving you the dictionary is something you control, have it switch to passing back Ints instead of Strings, and stop dealing with optionals if you don't have to.
I'm pretty new to Swift, and I'm trying to store my data as a 2D AnyObject array. I have declared var internalData = [[AnyObject]]() and a struct like so:
struct WiFiData {
var latency: Double
var duration: Double
}
Now, in another function, I would like to switch on the instance variable currentExit, which is an enum:
private var currentExit = Exit.A
enum Exit {
case A
case B
case C
case NotSelected
}
func someFunc() {
switch self.currentExit {
case .A:
self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) // <- CRASHES ON THIS LINE
......// other cases
}
However, it always crashes on the line self.internalData[0].append(WiFiData(latency: 1.5, duration: 4.0) as AnyObject) with the following error:
fatal error: Index out of range
Can anyone tell me why and how I could fix it? Any help is appreciated, thanks!
You instantiated a 2d array with [[AnyObject]](), but it's still empty, meaning there's nothing at self.internalData[0] for you to append to. There are a lot of things you could do to handle this, but if you know you're going to have 3 arrays inside self.internalData you may as well instantiate each internal array like so:
self.internalData = [[AnyObject](), [AnyObject](), [AnyObject]()]
Now you do have an empty array at self.internalData[0] that you can append items to.
It seems, though, like if you already know you're going to have 3 arrays of WiFiData it'd be even better just to create 3 variables:
var a = [WiFiData]()
var b = [WiFiData]()
etc.
and then you can access the individual arrays by name.
There are no "2D arrays" in Swift. There are arrays of arrays, which are different. For example, there is no promise that each row will have the same number of columns in an array of arrays.
In your case, the problem is that internalData has no elements in it. There has to be an internalData[0] element already created before you can append to it. You're assignment of var internalData = [[AnyObject]]() means there's no element 0. It's an empty array (of arrays).
Also keep in mind that the type [[AnyObject]] is very likely to cause a lot of problems for you. AnyObject is useful for working with Cocoa APIs, but generally creates a lot of headaches and should very rarely be part of a Swift property. You should almost certainly create some more specific type to hold your data. It's unclear from your example what you expect internalData to hold. Your code suggests, though, that you mean something more like:
var internalData: [Exit: [ExitRecord]] = [:]
enum Exit {
case A
case B
case C
case NotSelected
}
enum ExitRecord {
case wifi(latency: Float, duration: Float)
}
func someFunc() {
switch self.currentExit {
case .A:
var currentRecords = internalData[.A] ?? []
currentRecords.append(.wifi(latency: 1.5, duration: 4.0))
internalData[.A] = currentRecords
......// other cases
}
I'm trying to write an extension to Array in Xcode's playground. I want to write a function that will modify the array so that it will be filled with 0s when the function is called. The code I'm attempting to use is this:
import Foundation
extension Array {
mutating func zero() {
for i in 0..<self.count {
self[i] = 0 // ERROR - Ambiguous reference to member 'subscript'
}
}
}
This code does not run, because of the error at:
self[i] = 0
However, if I try to 'get' a value from self, I have no issues. For example:
import Foundation
extension Array {
mutating func zero() {
for i in 0..<self.count {
print(self[i])
}
}
}
has no errors and runs as expected.
So my question is; Why can't I modify the array?
Also, Replacing:
self[i] = 0
with,
self.append(0)
also results in an error. (Cannot invoke 'append' with an argument list of type '(Int)')
So it won't let me modify self at all it seems.
It will work if you do the following:
extension Array where Element: IntegerLiteralConvertible {
mutating func zero() {
for i in 0..<self.count {
self[i] = 0
}
}
}
You must constrain the type of the elements allowed, because you can't, for example, zero out an array of Strings.
It's important to remember, when extending something like an Array, that you take into account what types of elements are valid for the method you're adding. If it requires elements of a certain type, you constrain on it and then it will work. That way you can only use your zero() method on Arrays containing ints. You might define a different version for an array of Strings that replaces everything in the array with the string "zero", for example, and that implementation will only be used on the type that you constrain it to as well.
I'm super new to programming so this may be a pretty basic question.
I have an array of NSButtons (checkboxes) in my ViewController that I am calling "buttonArray." I want to go through the array, find out which buttons are checked and add the indexes of those checked buttons to another array of integers (located in a struct), which I am calling "intArray." This is the struct:
struct GetInterval {
var intArray = [Int]()
mutating func putIntIntoArray (intToAdd:Int) {
self.intArray.append(intToAdd)
}
}
I have tried doing this two ways (shown below), both of which have given me compiler errors.
First, I tried to use a function to import the index as an Int and add it to the "intArray" array...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.putIntIntoArray(Int(intervalIndex!))
}
}
...which gave me the error: "Cannot invoke 'putIntoArray' with argument list of type ((int))"
When that didn't work, I tried appending it directly from the "if" statement in the ViewController...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.intArray.append(intervalIndex!)
}
}
...which gives me the error: "GetInterval.Type does not have a member named 'intArray'"
How can I fix this?
When you say GetInterval.(something), this refers to a static member (a.k.a., something owned by the GetInterval type). However, your intArray is a member owned by each instance of the GetInterval type. So you need to create an instance first. For example:
var getInt = GetInterval() // create a new instance
getInt.putIntIntoArray(...) // modify this instance, by calling a mutating function
getInt.intArray.append(...) // modify this instance, by directly accessing a property
How would I declare an array in Swift which can hold values of any enum String type?
Here's what I want to do:
enum MyEnumType1: String {
case Foo = "foo"
case Bar = "bar"
}
enum MyEnumType2: String {
case Baz = "baz"
}
// ...
// Compiler error: "Type of expression is ambiguous without more context"
var myArray = [ MyEnumType1.Bar, MyEnumType2.Baz ]
// ^ need to declare type here, but not sure of correct syntax
// pass array over to a protocol which will iterate over the array accessing .rawValues
The two enum types are loosely related but definitely distinct and I need to keep the values separated in this instance, so lumping them all together in one enum and declaring the array of type MyAllIncludingEnumType is not desirable.
Or should I just declare an array of Strings and add the rawValues directly?
I could declare the array as [AnyObject] but then I'd have to type check each element before attempting to access the .rawValue, which isn't great either.
Currently I'm only able to use Swift 1.2 on this project, as it's for an app which is already in the App Store and I need to be able to ship updates before Xcode 7 goes GM.
Or is there a cleaner but completely alternate solution to what I want to do?
An alternative to Kametrixom's answer is to make both enums conform to a common protocol. Both automatically conform to RawRepresentable, because of the raw value of String:
protocol RawRepresentable {
typealias RawValue
var rawValue: RawValue { get }
...
}
However, you cannot use this as the type stored in the array since RawRepresentable is a generic protocol. Instead you could do:
protocol StringRepresentable {
var rawValue: String { get }
}
enum EnumA: String, StringRepresentable {
case A = "A"
}
enum EnumB: String, StringRepresentable {
case B = "B"
}
let array: [StringRepresentable] = [EnumA.A, EnumB.B]
array[0].rawValue // A
Just think about it logically: You want to store multiple enums in an array, so it can be either this or that enum, which is just what an enum is! You can declare an new enum that has associated values of all the accepted other enums like so:
enum A {
case A1, A2
}
enum B {
case B1, B2
}
enum All {
case First(A)
case Second(B)
}
Then you can create an array like this:
let array : [All] = [
.First(.A1),
.Second(.B2),
.Second(.B1),
.First(.A1)
]
Try The Below Code
enum MyEnumType1: String {
case Foo = "foo"
case Bar = "bar"
}
enum MyEnumType2: String {
case Baz = "baz"
}
var myArray: [Any] = [ MyEnumType1.Bar, MyEnumType2.Baz ]
If you use the array only to retrieve the rawValues of its elements, then you might simply store the rawValues in the array:
var myArray = [ MyEnumType1.Bar.rawValue, MyEnumType2.Baz.rawValue ]
If instead you want to retrieve the original enum from the array then you will need to type check the elements anyway, so var myArray: [Any] = ... will not make things worse.