With this code I enable a button if any form value is not nil:
myButton.isEnabled = !myForm.values.contains(where: { $0 == nil })
Is there any way to add multiple checks? I want to enable my button:
if the value of any field is nil (done, as you can see above);
if the value of any field is > 0;
if the value of any field is < 2;
(that is, the value should NOT be nil and should NOT be outside the range 0.0 - 2.0)
Can I still use contains(where:) to do it?
MyForm is a dictionary like this one:
["oct": nil,
"jan": Optional(3666.0),
"nov": nil,
"apr": nil,
"sep": nil,
"feb": nil,
"jul": nil,
"mar": nil,
"dec": nil,
"may": nil,
"jun": nil,
"aug": nil]
(strange enough, due to the behavior of a 3rd party library, it accepts nil values)
Yes, you can. If you want to enable the button if there is at least one Double value in range 0.0..<2.0 you don't need to call values. You can call contains on the dictionary directly. The where keyword is syntactic sugar and can be omitted when using trailing closure syntax.
The code uses the pattern matching operator to check the range.
myButton.isEnabled = myForm.contains { (_, value) in
guard let value = value as? Double else { return false }
return 0.0..<2.0 ~= value
}
If you want to enable the button if all values are Double in the given range you have to check inverted
myButton.isEnabled = !myForm.contains { (_, value) in
guard let value = value as? Double else { return true }
return !(0.0..<2.0 ~= value)
}
You can do it just by filtering all values like this:
myButton.isEnabled = !myForm.values.filter {
if let value = $0 { return value.field > 0.0 && value.field < 2.0 }
return false
}.count > 0
contains function is not appropriate solution in your case, because it should be used when you need to check if array contain some specific element. But in your case you need to check if values is in specific range.
It is possible to use contains function but it is not correct way
Related
In Swift 3, this is a compile error, if I use > or <
let a: Int?
guard a > 0 else {return}
guard a < 0 else {return}
Compile error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
But it's okay if I compare with == or !=
let a: Int?
guard a == 0 else {return}
guard a != 0 else {return}
It makes perfect sense for the equality operator to support optionals, because it's absolutely clear that for any integer valued variable i:
nil == nil
nil != i
i != nil
i == i if and only if their values are the same
On the other hand, it's not clear how comparison to nil should act:
Is i less than nil?
If I want to sort an array so that all the nils come out at the end, then I would want i to be less than nil.
But if I want to sort an array so that all the nils come out at the start, then I would want i to be greater than nil.
Since either of these are equally valid, it wouldn't make sense for the standard library to favor one over the other. It's left to the programmer to implement whichever comparison makes sense for their use-case.
Here's a toy implementation that generates a comparison operator to suite either case:
func nilComparator<T: Comparable>(nilIsLess: Bool) -> (T?, T?) -> Bool {
return {
switch ($0, $1) {
case (nil, nil): return false
case (nil, _?): return nilIsLess
case (_?, nil): return !nilIsLess
case let (a?, b?): return a < b
}
}
}
let input = (0...10).enumerated().map {
$0.offset.isMultiple(of: 2) ? Optional($0.element) : nil
}
func concisePrint<T>(_ optionals: [T?]) -> String {
return "[" + optionals.map { $0.map{ "\($0)?" } ?? "nil" }.joined(separator: ", ") + "]"
}
print("Input:", concisePrint(input))
print("nil is less:", concisePrint(input.sorted(by: nilComparator(nilIsLess: true))))
print("nil is more:", concisePrint(input.sorted(by: nilComparator(nilIsLess: false))))
Output:
Input: [0?, nil, 2?, nil, 4?, nil, 6?, nil, 8?, nil, 10?]
nil is less: [nil, nil, nil, nil, nil, 0?, 2?, 4?, 6?, 8?, 10?]
nil is more: [0?, 2?, 4?, 6?, 8?, 10?, nil, nil, nil, nil, nil]
Optional equality works logically, comparison doesn't.
5 == 5 = true
5 == nil = false
5 == 6 = false
nil == nil = true
Those all make sense, but these don't:
6 > 5 = true
5 > 5 = false
5 > nil = ??
nil > 5 = ??
This type of comparison does not have a simple answer, nor will that answer be the same depending on the use case.
This is because Int and Int? are 2 different things.
According to the documentation, Int has overloads for < and > and some other operators, while Optional only has overloads for == and !=, see the documentation on Optional, the section talking about Comparing Optional Values.
In the code I'm not able to remove optional from the value inside the lbltotalamount.
The value in lblTotalAmount is not removing its optional value from it.
Why? The value in grandtotal gets optional removed but when I assign it to a label it returns an optional value again.
The lblTottalAmount is getting an optional value. I want to remove it.
if success == false {
var grandtotal: Any? = value["total"]
if grandtotal != nil {
print("O!O!O!O/\(grandtotal!)")
grandtotal = String(describing: grandtotal)
self.lblTotalAmount.text = ([grandtotal]) as! String // (here I am not able to remove optional)
}
The problem is in the line
grandtotal = String(describing: grandtotal)
You check for nil but you don't unwrap the value so it's still an optional.
And you are misusing String(describing. Never use it for types which can be converted to String with an init method.
Use always conditional downcast
if success == false {
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
}
}
I have an if statement that checks to see if an array element matches a local variable.
if pinArray.contains(where: {$0.title == restaurantName})
How would I create a variable of this element?
I attempted
let thePin = pinArray.contains(where: {$0.title == restaurantName})
but this comes with "could not cast boolean to MKAnnotation".
I also tried variations of
let pins = [pinArray.indexPath.row]
let pinn = pins(where: pin.title == restaurantName) (or close to it)
mapp.selectAnnotation(thePin as! MKAnnotation, animated: true)
to no avail. What basic step am I missing?
contains(where:) returns a Bool indicating whether a match was found or not. It does not return the matched value.
So thePin is a Bool which you then attempt to force-cast to a MKAnnotation which of course crashes.
If you want the matching value, change your code to:
if let thePin = pinArray.first(where: { $0.title == restaurantName }) {
do {
mapp.selectionAnnotation(thePin, animated: true)
} catch {
}
} else {
// no match in the array
}
No need for contains at all. No need to cast (assuming pinArray is an array of MKAnnotation).
In Swift 3, this is a compile error, if I use > or <
let a: Int?
guard a > 0 else {return}
guard a < 0 else {return}
Compile error:
Value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
But it's okay if I compare with == or !=
let a: Int?
guard a == 0 else {return}
guard a != 0 else {return}
It makes perfect sense for the equality operator to support optionals, because it's absolutely clear that for any integer valued variable i:
nil == nil
nil != i
i != nil
i == i if and only if their values are the same
On the other hand, it's not clear how comparison to nil should act:
Is i less than nil?
If I want to sort an array so that all the nils come out at the end, then I would want i to be less than nil.
But if I want to sort an array so that all the nils come out at the start, then I would want i to be greater than nil.
Since either of these are equally valid, it wouldn't make sense for the standard library to favor one over the other. It's left to the programmer to implement whichever comparison makes sense for their use-case.
Here's a toy implementation that generates a comparison operator to suite either case:
func nilComparator<T: Comparable>(nilIsLess: Bool) -> (T?, T?) -> Bool {
return {
switch ($0, $1) {
case (nil, nil): return false
case (nil, _?): return nilIsLess
case (_?, nil): return !nilIsLess
case let (a?, b?): return a < b
}
}
}
let input = (0...10).enumerated().map {
$0.offset.isMultiple(of: 2) ? Optional($0.element) : nil
}
func concisePrint<T>(_ optionals: [T?]) -> String {
return "[" + optionals.map { $0.map{ "\($0)?" } ?? "nil" }.joined(separator: ", ") + "]"
}
print("Input:", concisePrint(input))
print("nil is less:", concisePrint(input.sorted(by: nilComparator(nilIsLess: true))))
print("nil is more:", concisePrint(input.sorted(by: nilComparator(nilIsLess: false))))
Output:
Input: [0?, nil, 2?, nil, 4?, nil, 6?, nil, 8?, nil, 10?]
nil is less: [nil, nil, nil, nil, nil, 0?, 2?, 4?, 6?, 8?, 10?]
nil is more: [0?, 2?, 4?, 6?, 8?, 10?, nil, nil, nil, nil, nil]
Optional equality works logically, comparison doesn't.
5 == 5 = true
5 == nil = false
5 == 6 = false
nil == nil = true
Those all make sense, but these don't:
6 > 5 = true
5 > 5 = false
5 > nil = ??
nil > 5 = ??
This type of comparison does not have a simple answer, nor will that answer be the same depending on the use case.
This is because Int and Int? are 2 different things.
According to the documentation, Int has overloads for < and > and some other operators, while Optional only has overloads for == and !=, see the documentation on Optional, the section talking about Comparing Optional Values.
Is there any difference between these two syntaxes? If not, any benefit?
if let userName = userNameTextField.text where userName.characters.count > 0,
let password = passwordTextField.text where password.characters.count > 0,
let confirmation = confirmationTextField.text where confirmation == password
else {
return false
}
and:
if userNameTextField.text?.characters.count > 0 &&
passwordTextField.text?.characters.count > 0 &&
confirmationTextField.text == passwordTextField.text
{
return false
}
First of all, note that where clauses in optional binding conditions is deprecated in Swift 3.0, replaced by ,.
Evolution Proposal SE-0099: Restructuring Condition Clauses
I.e.
let opt: Int?
/* Swift < 3 */
if let opt = opt where opt == 42 { /* ... */ }
/* Swift >= 3 */
if let opt = opt, opt == 42 { /* ... */ }
Secondly, your second example block wont compile in Swift >= 3.0, as the optional result from the optional chaining is not unwrapped; comparing optionals to literals has been removed in Swift 3.0 (thanks #MartinR), as per the following implemented proposals:
Evolution proposal SE-0121: Remove Optional Comparison Operators
Evolution proposal SE-0123: Disallow coercion to optionals in operator arguments
userNameTextField.text?.characters.count > 0 &&
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
this is an optional that, for Swift >= 3.0, needs to be unwrapped
prior to comparing it to the integer literal */
Now, proceeding to answer your "is there any difference ..." question, assuming we look at your two options fixed for Swift 3.
You could choose to bind the value of the optional String property text of userNameTextField if you'd like to use it in the if block that follows, or
If youd only want to ascertain that the text property is not nil or empty (""), you could omit the binding in favour or simply checking its character.count
E.g.:
struct Foo {
var bar: String?
}
var foo = Foo()
/* if you want to use foo.bar within the if block */
if let str = foo.bar, str.characters.count > 0 {
// do something with str ...
print(str)
}
/* if you only need to ascertain foo.bar is not nil
and not empty */
if (foo.bar?.characters.count ?? 0) > 0 {
// do something with str ...
print("not empty")
}
// alternatively ... (not nil or empty)
if !(foo.bar?.isEmpty ?? true) {
// do something with str ...
print("not empty")
}
If you simply want to ascertain the latter and return false in case the ascertation fails, you could prefer using a guard statement instead of if:
guard (foo.bar?.characters.count ?? 0) > 0 else { return false }
// ... if no false return, proceed with app flow here ...
// alternatively ...
guard !(foo.bar?.isEmpty ?? true) else { return false }
Neither version works in Swift 3. And I think in the first version, you meant to use guard instead of let.
With Swift 3, you want to do the following:
guard
let userName = userNameTextField.text,
let password = passwordTextField.text,
let confirmation = confirmationTextField.text,
userName.characters.count > 0,
password.characters.count > 0,
confirmation == password
else {
return false
}
There are problems with using your approach in Swift 3 as explained by some of the other answers. But if you are using Swift 2, the following is my answer to your question.
The two are exactly the same with only one difference. Both approaches are testing for a nil value and then introducing a boolean test if the value is not nil. For example in your line,
if let userName = userNameTextField.text where userName.characters.count > 0
You are testing to see if userNameTextField is not nil and then you are testing if its count is greater than zero. The same thing applies this line,
if userNameTextField.text?.characters.count > 0
except it is much shorter and readable.
Because both approaches are within an 'if' statement, they both evaluate to true or false and achieve the same purpose.
However, what separates them is that with the 'where' clause, you can introduce a Boolean test after a binding or pattern, regardless of whether or not there's an underlying semantic link between the two. So I can basically do this with the 'where' clause,
if let userName = userNameTextField.text where age > 0
There is no semantic link between userNameTextField and age. As you can imagine, that might not be what you intended and this can quickly lead to errors.