Suppose I have a class.
I want to give an error in init if my class doesn't follow the described rule
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) {
assert(array.count == 9, "Array should be lenght 9")
assert(array.index(of: 0) != nil, "There should ne 0 in array")
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
Then I need to create several instances of this class in a loop. Some of them, won't satisfy the condition, I described in init and complier and I will get an error.
But what I want is to skip creation of this instances without executing an error. And I want to keep checking condition logic inside init.
My initial idea might be wrong, but I'll appreciate if you help me to make it more properly.
You could use a failable initialiser to accomplish this, your object will be nil if you don't satisfy your checks.
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init?(array: [Int]) {
guard array.count == 9, array.index(of: 0) != nil else {
return nil
}
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
I disagree with #colmg that you should use failable initialiser, because it's destroys information about what actually went wrong.
Instead you should use throwable initalizer:
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) throws {
try assert(array.count == 9, "Array should be lenght 9")
try assert(array.index(of: 0) != nil, "There should ne 0 in array")
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
Assuming that assert is not standard version here, but this function:
struct AssertError: Error {
let description: String
}
func assert(_ condition: #autoclosure () -> Bool, _ description: String) throws {
if !condition() {
throw AssertError(description: description)
}
}
Now you can do this:
do {
let puzzle = try Puzzle([0, 1, 2, 3, 4])
} catch let error {
// Here you can handle error, and see what exactly went wrong, instead of just knowing that initialisation failed
}
Or you can use more advanced version:
func assert(_ condition: #autoclosure () -> Bool, _ error: Error) throws {
if !condition() {
throw error
}
}
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) throws {
try assert(array.count == 9, PuzzleError.invalidArrayLength)
try assert(array.index(of: 0) != nil, PuzzleError.arrayContainsZero)
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
enum PuzzleError: Error {
case invalidArrayLength
case noZeroInArray
}
}
Related
I have Struct init and I'm trying to filter the an array in init:
public struct doSomething: Codable {
public var listOfStuff: [String]
init(someStuff: [String]) {
var clone = someStuff
let stuff: [String] = clone.removeAll { $0 == "myName"}
listOfStuff = stuff
}
}
On this line let stuff: [String] = clone.removeAll { $0 == "myName"} I'm getting this error:
error: cannot convert value of type '()' to specified type '[String]
Any of you knows why I'm getting this error or if you know a work around?
I'll really appreciate your help.
removeAll does not return a value. That's the same thing as returning Void, which is the same thing as ().
clone.removeAll { $0 == "myName" }
listOfStuff = clone
Better yet, don't even use it.
init(someStuff: [String]) {
listOfStuff = someStuff.filter { $0 != "myName" }
}
Is there any pretty way to test the below? I have multiple parameters which I need to know if any one of them is nil
This is what I am using now, I am sure there is an efficient way to test all and type nil once but not sure how:
if title == nil || name == nil || height == nil || productName == nil {
//Do something
}
I am using ObjectMapper and at they moment, they don't support error handling, hence, my init() throws errors and I need to check if the values from Map are nil or not and through if they are.
I have created a simple extension on CollectionType to check for a collection of Optional value, if at least one element is not nil, if all elements have value or if none have value.
extension CollectionType where Generator.Element == Optional<AnyObject>, Index.Distance == Int {
func allNotNil() -> Bool {
return !allNil()
}
func atleastOneNotNil() -> Bool {
return self.flatMap { $0 }.count > 0
}
func allNil() -> Bool {
return self.flatMap { $0 }.count == 0
}
}
var title: String? = ""
var name: String? = ""
var height: Float? = 1
var productName: String? = ""
[title, name, height, productName].allNotNil()
[title, name, height, productName].atleastOneNotNil()
[title, name, height, productName].allNil()
In your case, you could use it like this,
if [title, name, height, productName].atLeastOneNotNil() {
}
Or, you could discard the extension above and simply use it like this,
if [title, name, height, productName].flatMap { $0 }.count > 0 {
}
For Swift 4,
extension Collection where Element == Optional<Any> {
func allNotNil() -> Bool {
return !allNil()
}
func atleastOneNotNil() -> Bool {
return self.flatMap { $0 }.count > 0
}
func allNil() -> Bool {
return self.flatMap { $0 }.count == 0
}
}
Updates for Swift 5,
Few new functions have been added to CollectionType such as first(where:) and allSatisfy(where:) and it is used here.
extension Collection where Element == Optional {
func allNil() -> Bool {
return allSatisfy { $0 == nil }
}
func anyNil() -> Bool {
return first { $0 == nil } != nil
}
func allNotNil() -> Bool {
return !allNil()
}
}
Here's a short version using a collection literal:
let isAnyNil = ([title, name, height, productName, nil] as [Optional<Any>]).contains { $0 == nil }
It's similar to #GeneratorOfOne's flatMap and count variant. I prefer the simplicity of contains.
If you do this often, I'd go with a free function to avoid the need to specify the type:
func isAnyNil(optionals: Optional<Any> ...) -> Bool {
return optionals.contains { $0 == nil }
}
isAnyNil(title, name, height, productName)
I'm not sure why you need to know, but if it is kind of unwrapping than it better to do so in Swift 2.0
if let email = emailField?.text, password = passwordField?.text {
//here you have both email & password
}
if you enter a method and need to do something in case any of them is nil, I would recommend using a guard:
guard let email = emailField?.text else {
// It is nil, do something
return
}
// if we got here, we have 'email' and it is not nil.
Side Note:
I'm guessing when you mean efficient you really talk about pretty or easy and not really efficient, because in either cases you would have to evaluate all arguments to see if they are nil.
If indeed you just want it to be pretty, you could use .filter to check
var nilElements = [email,password].filter{0 == nil}
you will get back only the elements which are nil
This method was working while I was using Swift 1.2. But now, I had to update to Xcode and I had to switch my language to Swift 2. This is the method from swift 1.2 which I used well ;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
var results: Payment?
if let item = query.first {
results : T = Payment(id: item[id], imageName: item[image], type: item[type], deliveredPriceStr: item[deliveredPrice], orderID: item[orderId])
}
return results
}
Now I modified it for Swift 2 but couldn't manage;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
do {
let query = try table.filter(id == idToFind)
let item = try SQLiteDataStore.sharedInstance.SADB.pluck(query)
if try item != nil {
let results : T = Payment(id: item[id], imageName: item[image], type: item[type], deliveredPriceStr: item[deliveredPrice], orderID: item[orderId])
} else {
print("item not found")
}
} catch {
print("delete failed: \(error)")
}
}
return results
}
And I'm getting this error : "Cannot subscript a value of type Row" . My item's data type seems like changed to Row. How can I parse it ? What should I do ?
PS: I'm using swift2 branch.
Finally I figured out how to get values. It's easy now the row item has get method on swift-2 branch. So new method is ;
static func findById(idToFind : Int64) -> T? {
let query = table.filter(id == idToFind)
var results : T?
do {
let query = table.filter(id == idToFind)
let item = SQLiteDataStore.sharedInstance.SADB.pluck(query)
if try item != nil {
results = Payment( id: (item?.get(id))!, imageName: (item?.get(image))!, type: (item?.get(type))!, deliveredPriceStr: (item?.get(deliveredPrice))!, orderID: (item?.get(orderId))!)
} else {
print("item not found")
}
}
catch {
print("delete failed: \(error)")
}
return results
}
Hope that this helps someone.
Is it possible to count all properties who are not nil?
For example:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
}
var test = test()
test.val1 = 1
test.val2 = 2
How to find out that 2 properties are set? I could check for each one with (!= nil) - but is there an easier (and better) way?
You can do this manually, with a convenience method:
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5]
return flatMap { $0 }.count
}
flatMap(_:) takes a closure that takes a single element of the array and returns an optional value (Element -> T?), and returns the result of applying that closure to each element of the array, with nil values ignored.
The only way to make this simpler would be to store your values as an array of optional Ints in the first place:
class Test {
var vals: [Int?]
}
You can then still access each individual value using the array subscript notation (let val2 = vals[1]). You could then just use the second line in the convenience method above (filter then count) to get the number of non-nil values:
let nonNilCount = vals.flatMap { $0 }.count
If your values are of different types, this approach will still work if you cast the array to a type that encompasses all the different types:
class Test {
var val1: Int?
var val2: Double
var val3: String
var val4: MyRandomClass?
func numberOfNonNil() -> Int {
let vals = [val1, val2, val3, val4, val5] as [Any?]
return flatMap { $0 }.count
}
}
This works because all the values can be expressed as the type Any?.
I don't think there's a way to do this, but you can implement your own function like this:
class test {
var val1:Int?
var val2:Int?
var val3:Int?
var val4:Int?
var val5:Int?
func setVarsCount() -> Int {
var setVariablesCount = 0
if val1 != nil {
setVariablesCount++
}
if val2 != nil {
setVariablesCount++
}
if val3 != nil {
setVariablesCount++
}
if val4 != nil {
setVariablesCount++
}
if val5 != nil {
setVariablesCount++
}
return setVariablesCount
}
}
#Stuarts answer is good, but you have to know the properties of the class, and if you add another property to the class, you also have to modify your method. To avoid this problem, you can use reflection, like
Swift 1.2:
func numberOfOptionalProperties() -> Int {
let mirror = reflect(self)
var numberOfOptionalProperties = 0
for index in 0..<mirror.count {
if mirror[index].1.disposition == .Optional {
++numberOfOptionalProperties
}
}
return numberOfOptionalProperties
}
Swift 2.0:
func numberOfOptionalProperties() -> Int {
return Mirror(reflecting: self).children.reduce(0) {
Mirror(reflecting: $1.value).displayStyle == .Optional ? $0 + 1 : $0
}
}
i have a fixed size array as
var fieldNameArray = [String?](count: 4, repeatedValue: nil)
i am doing this to search if there is the element in array or not
if let temp = find(fieldNameArray,"profile_picture"){//i get a compile error here
//remove the data
....
}else{
println(" //append the value")
.....
}
But i get a compile time error as
Cannot invoke 'find' with an argument list of type '([(String?)],
String)'
I think i should unwrap it? How can i do it
UPDATED
SRWebClient.POST(registerURl)
.data(registerImagesArray, fieldName: fieldNameArray, data: parametersToPost)
.send({(response:AnyObject!, status:Int) -> Void in//here compile time error
println("response object: \(response)")
Again after i changed my array to fixed size array i got this error
Cannot invoke 'send' with an argument list of type '((AnyObject!, Int)
-> Void, failure: (NSError!) -> Void)
For efficiency you should not do what Manav Gabhawala suggests but write a find function yourself:
func myFind(array: [String?], value: String) -> Int? {
for (i, av) in enumerate(array) {
if av != nil && av! == value {
return i
}
}
return nil;
}
As Swift compiles to machine code, you will have nearly the same performance as with the standard library’s find.
Try using this instead (Swift 2.0):
if let index = fieldNameArray.indexOf("profile_picture") {
//remove the data using the index
....
} else {
print("// append the value")
.....
}
In swift 1.2 (a little inefficient but it works) :
if let temp = find(fieldNameArray.filter { $0 != nil}.map { $0! },"profile_picture") {
// Then same code as question...