Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.rangeOfString() with switch in Swift

I want to check if my input has a rangeOfString must with a lot of different strings to check.

basically this if statement but using a switch to check a big list of different strings

if (input.rangeOfString("lol") != nil) {
    println("yes")
}

I've tried doing this but it isn't working.

switch input {
case rangeOfString("lol"):
    println("lol")
case rangeOfString("dw"):
    println("dw")
default:
    println("Default")
}
like image 562
Stef Kors Avatar asked Apr 13 '15 15:04

Stef Kors


2 Answers

You misunderstood the idea of a switch statement. It checks the value of one expression against multiple possible values, and optionally lets you access associated values of an enumeration. It does not let you pass a single value to multiple expressions listed as switch cases*, and pick whichever provides the first match.

You need to make a chain of if-elses to make your code work:

if (input.rangeOfString("lol") != nil) {
    println("lol")
} else if (input.rangeOfString("dw") != nil) {
    println("dw")
} else {
    println("Default")
}

I would like to check if a word is being used in the input and I have like 50 words I'd like to check.

Then the switch is not a good alternative either. Make a container of the words you wish to search, and use filter to find all matches:

let input = "hahalolblah"
let words = ["lol", "blah", "hello"];
let matches = words.filter {input.rangeOfString($0) != nil}
println(matches) // Produces [lol, blah]

* It turns out that switch lets you pass one expression to an override of the ~= operator, along with values coming from cases of the switch. See this answer for details.

like image 153
Sergey Kalinichenko Avatar answered Oct 23 '22 05:10

Sergey Kalinichenko


While the others answers are probably right about if being a better way to go, you can do something like this via heroic abuse of the ~= operator:

import Foundation

struct Substring {
    let substr: String
    init(_ substr: String) { self.substr = substr }
}

func ~=(substr: Substring, str: String) -> Bool {
    return str.rangeOfString(substr.substr) != nil
}

let input = "contains wat"

switch input {
case Substring("lol"), Substring("wat"):
    println("huh?")   // this one is picked
case Substring("dw"):
    println("dw")
// you can also mix and match
case "Explicit full string":
    println("matches whole string")
default:
    println("Default")
}

Switch statements in Swift can be extended via overloading of the ~= operator. So for example, the reason this works:

switch 2.5 {
case 0...5: println("between nought and five")
default:    println("not in range")
}

is because there is a definition of the ~= operator that matches any kind of Comparable type to an interval:

func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool

Here, by creating a new type of Substring, I've created a way to match strings to substrings.

In theory you could skip the creation of the Substring type and do the following:

func ~=(substr: String, str: String) -> Bool {
    return str.rangeOfString(str) != nil
}

let input = "contains lol"

switch input {
case "lol":
    println("lol")
case "dw":
    println("dw")
default:
    println("Default")
}

This would work, but would be a bad idea because now you'll have changed the way switches on strings work universally so that partial matches are always true, which could lead to some unpleasant and unexpected behaviour elsewhere in your code.

like image 39
Airspeed Velocity Avatar answered Oct 23 '22 07:10

Airspeed Velocity