Swift: Can not use array filter in if let statement condition - swift

Suppose I have an array of user's names
let users = ["Hello", "1212", "12", "Bob", "Rob"]
I want to get the first user whose name length is 2, so I filtered the array and got the first user
if let selected = users.filter{$0.characters.count == 2}.first {
print(selected)
}
This code is throwing a compilation error under swift 2.2
Consecutive statements on a line must be separated by ';'
However, this is working fine though
let selected = users.filter{$0.characters.count == 2}.first
if let selected = selected {
print(selected)
}
Can anyone explain why do I need to store filter result in a separate variable first? Any help would be really appreciated.

You can make this work by putting parentheses around the closure that you're passing to filter:
if let selected = users.filter({$0.characters.count == 2}).first {
print(selected)
}
That is the right way to do it. The trailing closure syntax doesn't work very well sometimes on lines with extra elements. You could also put parentheses around the whole statement:
if let selected = (users.filter {$0.characters.count == 2}.first) {
print(selected)
}
Swift is just having trouble parsing your statement. The parentheses give it help in how to parse the line. You should prefer the first way since the closure is indeed a parameter of filter, so enclosing it in parentheses makes it clear to Swift that you are passing it to filter.

Related

How to do unwrappin inside If-let statement?

I am using if let for getting the object if its not nil. But I also need to check other condition as well i.e., if "treatmentContext.patientTreatment.canWritePermissions.contains(treatmentContext.pathPatientTreatment.owner". That I am able to do by putting comma after the first statement but here the issue is, I need to unwrap the value of treatmentContext.pathPatientTreatment.owner and here I don't know where exactly I need to unwrap that so that my if condition gets pass when it meets all the criteria.
Below is the code for reference.
if let treatmentContext = IoC.resolve(Treatment.self, from: .treatment), treatmentContext.patientTreatment.canWritePermissions.contains(treatmentContext.pathPatientTreatment.owner)
{
self.presentNavigation(isNew: isNew)
}
You already know you can separate the conditions with ,, so just do that again, but this time it's another optional binding clause:
if let treatmentContext = IoC.resolve(Treatment.self, from: .treatment),
let owner = treatmentContext.pathPatientTreatment.owner,
treatmentContext.patientTreatment.canWritePermissions.contains(owner) {
self.presentNavigation(isNew: isNew)
}
You can separate any number of optional binding clauses, Bool conditions, or case ... patterns with , in an if.

swift 5.1 evaluate a closure in the wrong way

I'm having an issue with evaluation of one line of code
if i break it down to two lines, it's working , but in one line of code, it's just evaluate in a 'new' to a 'wrong' way.
my main reason for asking this question, is not to solve it, I know I can use parenthesis to solve it, and break it to Two line, but don't want to solve it, I just want to know why its evaluated like this , and if there's a solution for this : some setting to patch , in Order THAT it will work in ONE LINE OF CODE :
Heres the code that working in Two lines
Heres the code that trying to do the same thing, but rise an error as you can see:
full code of both working and not working :
class ClosuresStack {
var dic = Dictionary<String,(()->String)->String >()
subscript(_ str:String)-> (()->String)->String {
get {
return dic[str]!
}
set {
dic[str] = newValue
}
}
}
func createClosuresStak() -> ClosuresStack {
let cs = ClosuresStack()
func takesAClosureReturnA_string(_ closure:()->String) ->String {
return closure() + " Two"
}
cs["C"] = takesAClosureReturnA_string
return cs
}
let c = createClosuresStak()["C"]
let str = c{"One"}
print(str) // print: One Two
let c = createClosuresStak()["C"]{"One"} // error -->
now, I want to somehow understand how to change it that it will work in ONE LINE OF CODE : meaning that the evaluation of 'createClosuresStak()["C"]{"One"}' will create a closure after ["C"] , and then from that point writing the {"One"}
will make it a full evaluate of the line :
let c = createClosuresStak()["C"]{"One"}
making 'c' a String
if that's not possible, I need to know it Too , tnx :)
UPDATE
tnx for the comments , its help me understand the problem more clearly :
1) im understanding that the createClosuresStak()["C"]{"One"}
acutely trying to add the string 'One' as another parameter to the sub script , and there for the error from the compiler was that is cannot subscript (String,()->String} , 'C' as the string inside the [] , and the other parameter {"One"} -> BUT , isn't that some kind of a bug?, been that i'm using [] ,Cleary the compiler need to 'understand' that I want to subscript a String, also by power of inferring that swift has,
2) now I'm still trying to get that syntax to work as it is so I try to change some things, in order to get it to work :
so I created a function that take a string, and return a dictionary of type : Dictionary<String,()->String>, and then trying so subscript it
and the compiler don't rise an error that way :
func closuresDictionary(_ s:String) -> Dictionary<String,()->String> {
var dic = Dictionary<String,()->String>()
func foo()->String {
return s + " Two"
}
dic["C"] = foo
return dic
}
let c = closuresDictionary("One")["C"]{ "SomeString" }
c is now a closure of type ()->String which does noting with string that I put inside, so the syntax works, but the outcome is not doing anything.
when im changing the return type of the dictionary to a different closure : (String)->String instead of ()->String , im getting the same old error, that I'm trying to subscript a (String,(String)->String)
and I need a function that will take the string inside the {} , and create something from it meaning that I need to subscript to return a closure of (String)->String
its seems like there's no way to do that
im adding two more pictures of my last trying in order to get this line of code in current syntax to work
the wanted syntax working but the outcome is not an outcome not doing any thing with the string inside the {}:
same error, by changing the function to (String)->String
Your example:
let c = createClosuresStak()["C"]{"One"}
is using trailing closure syntax.
Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:
Cannot subscript a value of type 'ClosuresStack' with an argument of type '(String, () -> String)'.
In other words, you can't pass both "C" and the closure {"One"} to the subscripting function.
There are at least 3 ways to fix this and still put it on one line:
Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax
Wrap the closure in () to make the call explicit:
let c1 = createClosuresStak()["C"]({"One"})
print(c1)
Option 2: Wrap the createClosureStak()["C"] in parentheses
That lets Swift know the subscripting only gets "C" as a parameter and allows trailing closure syntax to work as expected:
let c2 = (createClosuresStak()["C"]){"One"}
print(c2)
Option 3: Add .self to the result before the trailing closure syntax:
That again finishes the subscripting call and avoids the confusion.
let c3 = createClosuresStak()["C"].self {"One"}
print(c3)
Personally, I would choose option one, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.

Fixed length array and a forced unwrapping of the last and the first elements

I have an array with 3 elements and want to take the first one and the last one elements.
let array = ["a", "b", "c"]
let first: String = array.first!
let last: String = array.last!
SwiftLint mark a force unwrap as a warning. Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
I don't want to use a default values like in an example below
let first :String = array.first ?? ""
Edit:
Why am I asking about it? Because, I would like to avoid an warnings from the SwiftLint when using a forced unwrapping when asking for a first and a last element of an array which was defined by a literal and has enough elements to be sure that there is the first and the last element.
Edit 2:
I have found a name for what I was looking for. It's called Static-Sized Arrays. Static-Sized Arrays discussion stoped in 2017 and there is no chance to use it.
Try with index:
let first = array[0]
let last = array[array.count - 1]
Why am I asking about it? Because, I would like to avoid an warnings
from the SwiftLint when using a forced unwrapping when asking for a
first and a last element of an array which was defined by a literal
and has enough elements to be sure that there is the first and the
last element.
You can't really avoid to unwrap optional value, so if you only need it for two cases extensions can help here.
extension Collection {
func first() -> Element {
guard let first = self.first else {
fatalError() // or maybe return any kind of default value?
}
return first
}
}
let array = [1, 2]
array.first() // 1
And if it need to be only in one swift file you can place this code in that file and mark extensions with private keyword.
Can I avoid a forced unwrapping when asking about the first and the last elements for a well known (defined) arrays?
No you don't have to worry about it for a fixed array , actually the optional attachment for the properties first and last is designated to avoid crashes for an empty arrays

how to make sense of pattern matching of optionals in for loop in swift

in a practice problem I was asked to print out all elements that are not nil in an array of string, and I realize
for case let name? in names{
print(name)
}
would do the job. But isn't it counter-intuitive?
In the snippet, I read it as "for every element (with actual value or nil)that is in names", but in fact it should be "for every element (actual value)that is in names".
Can anyone help me to make sense of the snippet?
You want to know why this code:
let names = ["b", nil, "x"]
for case let name? in names {
print(name)
}
Produces this output:
b
x
You are wondering what happens to the nil.
The answer is the "Optional Pattern" found in the Language Reference:
The optional pattern provides a convenient way to iterate over an
array of optional values in a for-in statement, executing the body of
the loop only for non-nil elements.
The case keyword is vital. It changes the nature of the for loop significantly. As you can see from this complier error, name? inside the loop is not an optional at all.
Think of the ? as an operator that removes the optionality of name. If the assignment would result in nil, that iteration of the loop does not happen, and the next iteration starts.
Notice that without the case you do not get the same behavior at all.
This:
for name in names {
print(name)
}
Will get this output:
Optional("b")
nil
Optional("x")
And that neither of these work at all.
You can use filter with conditional and then print the result like this:
let nameNotNil = names.filter{$0 != nil} //filter all values diferent nil
print(nameNotNil) // can be nil if any didnt has strings

.prepare vs. .select

I have a working connection to a database in an iOS10 app, using SQLite.swift.
I want to select details for a specific university where I have an IHE_ID passed in from another view controller.
I would like to just select the row for that specific university, but I can't get a query to work. I can however loop through all the data with a prepare and then choose the one I need from that, which of course is more resource intensive than I need since I already have the specific IHE_ID passed in as anIHE Int from the sending view controller.
connection is working so omitting that code...
do {
let db = try Connection(destinationDBPath, readonly: true)
let IHEs = Table("IHE")
let IHE_ID = Expression<Int>("IHE_ID")
let IHE_Name = Expression<String>("IHE_Name")
let IHE_COE_Url = Expression<String>("IHE_COE_URL")
let IHE_Sector = Expression<Int>("IHE_Sector")
let IHE_COE_Name = Expression<String>("IHE_COE_Name")
for ihe in try db.prepare(IHEs){
if (ihe[IHE_ID] == anIHE){
// build this string, otherwise ignore rest of dataset (doing this because query isn't working)
buildIHE = "Name: \(ihe[IHE_Name])\n"
buildIHE.append("URL: \(ihe[IHE_COE_Url])\n")
// buildIHE.append("Sector: \(ihe[IHE_Sector])\n")
if (ihe[IHE_Sector] == 0) {
buildIHE.append("Sector: Public\n")
} else {
buildIHE.append("Sector: Private\n")
}
buildIHE.append("College of Education Name: \(ihe[IHE_COE_Name])\n")
}
}
print ("Got through building IHE portion of view")
What I'd like to do is use this instead of the for loop.
if let query = IHEs.select(IHE_ID,IHE_Name,IHE_COE_Url,IHE_Sector,IHE_COE_Name).filter(IHE_ID == anIHE){
print("Query successful for \(anIHE) with name \(query[IHE_Name])")
// more actions to build the string I need would then occur
} else {
print("Query has failed or returned nil")
}
Finally, I'll use the selected elements if I can get the query to work.
I think I probably just have something wrong with my syntax on the query, but any help is appreciated.
The line with the "if let query" has this error in Xcode:
Initializer for conditional binding must have Optional type, not 'Table'.
This leads me to think it's something with my use of the .select statement and just new to using SQLite.swift and swift in general.
Last thing is that anIHE comes into this function as an Int, and IHE_ID is Expression as shown in this code. I'm thinking this may be part of the problem.
The Initializer for conditional binding must have Optional type error means that the expression on the right of the if let v = expr statement is not an Optional: there is no point using if let, and the Swift compiler says that you should just write let v = expr.
And indeed, IHEs.select(...).filter(...) returns a non-optional value of type Table.
It is not the database row you expect, because the query has been defined, but not executed yet. After all, you weren't using db: where would the rows be loaded from?
The solution is to bring back the database connection, and load a single row. This is done with the pluck method.
if let ihe = try db.pluck(IHEs.select(...).filter(...)) {
print("Name \(ihe[IHE_Name])")
}