Cannot convert value of type 'String' to expected argument type 'Bool' - swift

I am trying to write a function that will return true if the String str starts with a vowel. the following code will compile fine
func beginsWithVowel(str: String) -> Bool {
if(str.characters.count == 0){
return false
} else if(str.characters[str.startIndex] == "a"){
return true
}
return false
}
beginsWithVowel(str: "apple")
the problem is when I compare the first character to more than one character, for example
else if(str.characters[str.startIndex] == "a" || "e" || "i")
then I get the error 'Cannot convert the value of type 'String' to expected argument type 'Bool''
I've been fiddling with the code but no luck so far, any help would be appreciated. Thank you.

Swift cannot infer the logic you are trying to make. The logic to Swift becomes something like this:
if(str.characters[str.startIndex] == "a" || "e" || "i")
is equivalent to if(<Boolean expression> || "e" || "i")
is equivalent to if(<Boolean expression> || <String expression> || String expression)
An alternative solution can be:
if(["a", "b", "c"].contains(str.characters[str.startIndex])){

You should write it like this:
else if(str.characters[str.startIndex] == "a" || str.characters[str.startIndex] == "e" || str.characters[str.startIndex] == "i")
You get the error, because the compiler tries to convert both "e" and "i" to type Bool.

Instead of using if else switch will be more efficient:
func beginsWithVowel(str: String) -> Bool {
guard str.characters.count > 0 else {
return false
}
switch str.characters[str.startIndex]{
case "a","e","i","o","u":
return true
default:
return false
}
}

When you perform "a" || "e" || "i" you are comparing between the strings. Use this code:
if(str.characters[str.startIndex] == "a"
|| str.characters[str.startIndex] == "e"
|| str.characters[str.startIndex] == "i") {
// Your Code...
}

The boolean OR operator || expects boolean expressions.
So you would have to write EXPR == "a" || EXPR == "e" || EXPR == "i" where EXPR is the expression to get the first character.
However there is an easier solution (code is Swift 4)
func beginsWithVowel(str: String) -> Bool {
return "aeiou".contains(String(str.prefix(1)))
}
It considers also the empty string case.

Related

Exceptions with if else statement in Scala

if ( option != "AB" || option != "BC"|| option != "CD") {
try {
var option = dbutils.widgets.get("option")
}
catch {
case e: Exception => println("The option shoud be following AB, BC or CD " + option)
}
}
I am trying to use exception in scala
However, the widget value is not throwing an exception when I use the value such as
"XY" or any other value than AB, BC or CD.
Please can someone let me know what I am doing wrong here. Thank you
If AB,BC & CD are the valid values, then your predicate should look like this:
if(option == "AB" || option == "BC" || option == "CD")
Also, you can use the require function to enforce the predicate and throw an exception if the input doesn't match:
def getWidget(option: String) = try {
require(option == "AB" || option == "BC"|| option == "CD", s"The option shoud be following AB, BC or CD instead got $option")
dbutils.widgets.get("option")
} catch { case e: Exception => println(e) }

How to compare two string indexes in swift 4

I'm trying to find whether the character "S" or "C" appears in a string. At least one will be in the string but not necessarily both.
let S = codeZ.characters.index(of: "S")
let C = codeZ.characters.index(of: "C")
if (C == nil) || S < C {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
else if S == nil || (C < S) {
nextView.sequential_flag = false
nextView.continuous_flag = true
}
I'm getting the error : Binary operator '<' cannot be applied to two 'String.CharacterView.Index?' (aka 'Optional') operands
In my experience with swift this usually means something else if wrong.
Also I've tried changing the if statements to this below.
if (C == nil) || S?.encodedOffset < C?.encodedOffset {
nextView.sequential_flag = true
nextView.continuous_flag = false
}
And I got the error : Binary operator '<' cannot be applied to two 'Int?' operands.
Any help on how to do this is much appreciated, thank you.
You should check if S is nil or not and provide a fallback value to C. Then you can compare the two non-optional values.
if let S = S, S.encodedOffset < (C?.encodedOffset ?? Int.max) {
nextView.sequential_flag = true
nextView.continuous_flag = false
}

Are "&&" and "," the same in Swift?

As the title says, I just came across a case where if && (AND) and , give the same result in Swift. I tested the code below:
let a = 5
let b = 6
if a<6, b>0 {
print("should be true")
}
if a<6, b<0 {
print("should be false")
}
if a>6, b>0 {
print("should be false")
}
if a>6, b<0 {
print("should be false")
}
It only logs:
should be true
So, the behavior of , is just like &&, am I correct?
They can be used in similar situations but that does not mean they are exactly the same.
Consider:
if (a && b) || c
you cannot write
if (a, b) || c
even a && b || c is different from a, b || c.
if a, b is something like
if a {
if b {
...
}
}
Both expressions have to be evaluated to true but they are still two separate expressions. We shouldn't imagine && there.
Why do we need the , operator?
The operator is needed to combine optional binding with boolean conditions, e.g.
if let a = a, a.isValid() {
becuase && wouldn't help us in such situations.
They're different in that a comma will take the lowest precedence possible. i.e everything else will be executed before the commas are checked.
// (true || false) && false
if true || false, false {
print("I will never be executed")
}
// true || (false && false)
if true || false && false {
print("I am always executed")
}

How does Swift handle if statements?

If I have the statement:
if object != nil && object.property == 2 {
//do something
}
does the if statement break as soon as it finds out object = nil?
Yes
When you concatenate a list of conditions C[0]...C[n] with the AND && operator, the runtime evaluates in order each condition and if a C[i] condition is found false, then the evaluation of the whole expression does end and it is judged false.
let c0 = true
let c1 = false
let c2 = true
if c0 && c1 && c2 {
print("Hello world")
}
In this case only c0 and c1 will be evaluated and the whole expression will be interpreted as false.
You can test it yourself in Playground.
c0 || c1 || c2
Symmetrically if you define an expression as the OR || concatenation of several clauses, then the whole expression is interpreted as true (and the evaluation of the clauses does stop) as soon as the first true condition gets found.
Yes is the answer.
Simple to try - see below - however note that as written your test won't work - a non-optional cannot be nil, and so you will have to unwrap it to test .property.
struct MyObj {
var property: Int
}
var object: MyObj? = nil
if object != nil && object!.property == 2 {
print("Not nil")
} else {
print("Nil") // Prints - would have crashed if test fully evaluated
}
object = MyObj(property: 2)
if object != nil && object!.property == 2 {
print("Not nil") // Prints
} else {
print("Nil")
}

In Swift, how do I sort an array of strings and have strings of numbers, symbols, etc. always come after alphabetic strings?

I want to sort an array of strings so that alphabetic characters are always before any other kinds of characters. For example:
["800", "word", "test"]
Should sort to:
["test", "word", "800"]
The alphabetic strings can have numbers in them, but they can't have them as the first letter. So for example, if it's "ab8s" that should still come before "dog".
How would I do this? The comparison operators in Swift by default sort numbers before letters.
The key is to write your "is ordered before" function to do whatever you want. For example, if by digits, you mean "0"..."9", then something like this is probably what you want:
func isDigit(c: Character) -> Bool {
return "0" <= c && c <= "9"
}
func sortedLettersFirst(lhs: String, rhs: String) -> Bool {
for (lc, rc) in zip(lhs.characters, rhs.characters) {
if lc == rc { continue }
if isDigit(lc) && !isDigit(rc) {
return false
}
if !isDigit(lc) && isDigit(rc) {
return true
}
return lc < rc
}
return lhs.characters.count < rhs.characters.count
}
words.sort(sortedLettersFirst)
Of course, if by "digit" you mean "unicode digits", then see What is the replacement for isDigit() for characters in Swift? for a different approach to isDigit. But ultimately, the point is to make whatever rule you want in your isOrderedBefore function, and pass that to sort().
How about this.
func sortedNumbersLast(words: [String]) -> [String] {
var startsWithDigit = [String]()
var startsWithCharacter = [String]()
for word in words {
if let first = word.characters.first {
if first >= "0" && first <= "9" {
startsWithDigit.append(word)
}
else {
startsWithCharacter.append(word)
}
}
}
return startsWithCharacter.sort(<) + startsWithDigit.sort(<)
}
Try this in Playground, as you can see in the console output it gets the job done, letters are being placed at the front, followed by numbers and other symbols are at the end.
let stringsSortedByLettersFirstNumbersThenAndOtherSymbolsAtTheEnd: (String, String) -> Bool = { s1, s2 -> Bool in
guard let f1 = s1.first, let f2 = s2.first else {
return s1 < s2
}
if f1.isLetter == false && f2.isLetter {
return false
}
if f1.isLetter && f2.isLetter == false {
return true
}
if f1.isNumber == false && f2.isNumber {
return false
}
if f1.isNumber && f2.isNumber == false {
return true
}
return s1 < s2
}
let str = ["4", "A", "B", "Z", "T", "3", "'", "8"]
print(str.sorted(by: stringsSortedByLettersFirstNumbersThenAndOtherSymbolsAtTheEnd))
Above method sorts String in ascending order, if you want to do the other way just change the lines
return s1 < s2
to
return s1 > s2