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

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

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.

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

Is Nil-Coalescing always needed when using firstIndex(of: Character) in Swift?

Excerpt from The Swift Programming Language (Swift 4.2) documentation by Apple.
let greeting = "Hello, world!"
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
The above example works totally fine except after I exclude the Nil-coalescing part of the code.
let beginning = greeting[..<greeting.firstIndex(of: ",")]
By leaving out the ?? greeting.endIndex, Xcode returns an error.
The question is why is it necessary?
Why can't we just use firstIndex() directly to access the substring.
As it may be no index for the supplied character , so following your way the app will crash
func firstIndex(of element: Character) -> String.Index?
The optional return String.Index? solves it , and that's why you need ??
if you don't supply ??
Then you have to force unwrap let beginning = greeting[..<index] here index should be index! which will cause a crash if it's nil
Consider this example.
let greeting = "Hey There!"
let index = greeting.firstIndex(of: ",")
index is of type Index?, so it can be nil. Since the string has no ',', the value of index is nil.
So this code becomes illegal. Since index cannot be an optional value.
let beginning = greeting[..<index]
You can alternatively unwrap it and get the index if it is valid index like this.
guard let indexFound = index else {
print("Character not) found in string"
return
}
let beginning = greeting[..<indexFound]
print("The first occurance of the character is at \(beginning).")
Real world example: (I know it's not good, but it should be enough :x)
Remember the bot captcha's that pop up in websites? The ones that ask
you to identify the number of cars, street signs etc. So there you
click on the boxes which show the requirement. and click 'submit'. But
if there aren't any, you click 'Skip'. So drawing parallels. If you
have a valid index, it returns the valid index, otherwise it return
nil which is not an valid Index, but it is a valid answer to the
question asked. (Is there a ',' inside the string?) The problem here is, the next part. Slicing an array requires you to have a valid Index, so your index currently is an optional which you can change to a valid index by safely unwrapping like my example or having a nil-coalescing check at the time of getting the index.
You should look up optionals in Swift to get a better understanding.

Swift 3 capitalize string

let first = postalText.text?[(postalText.text?.startIndex)!]
let second = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 1))!]
let third = postalText.text?[(postalText.text?.index((postalText.text?.startIndex)!, offsetBy: 2))!]
I'm trying to capitalize the FIRST and THIRD character and then merge all 3 into a new string
but the .uppercase and .capitalized doesn't work .
Also how do i check that the SECOND character is a number ?
.uppercased and .capitalized only work for strings, what you show there are Characters. You can cast a Character as a String and make it capitalized.
let firstCapitalized = String(first!).capitalized
If you want to check if a Character is an int, you can also make it a String, and then check if casting the String as an Int is non-nil:
if Int("\(second!)") != nil {
print("Is Integer")
}
These cases all assume your first, second, and third are all non-nil, and force-unwraps them.
EDIT
I had some free time and was overlooking some old posts on SO, and I realized this answer I posted isn't using the best coding form. First off, force unwrapping anything is always a bad idea (it's a recipe for a crash in the future), so for the first part. Do something like this:
let firstCapitalized = String(first ?? "").capitalized
This at least gives you a back-out in case first == nil then you'll just be stuck with an empty string.
For the second part, I would use optional unwrapping instead of if Int("\(second!)") != nil. I would say the more proper method would be something like this:
if let second = second, let stringConvertedToInteger = Int("\(String(second))") {
print("\(stringConvertedToInteger) is an integer")
} else {
print("Either second is nil, or it cannot be converted to an integer")
}
This will optionally unwrap the character second, and if it has a value, convert it to an integer (should it be one, checked by optional unwrapping). This is the safest way to do it, and will keep you from experiencing any runtime errors.

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

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.