I have a custom struct with a few fields, and I'd like to pattern match against it in a swift switch
statement so I can customise the matching, by comparing one of the fields against a regex.
E.g. Given this structure:
struct MyStruct {
let header: String
let text: String
}
I'd like to pattern match like this:
switch(someInstance) {
case ("h1", "[a-z]+"): ...
case ("h1", "0-9+"): ...
}
I tried to get this to work using a pattern matching function as follows:
func ~=(pattern: (String, String), value: MyStruct) -> Bool {
return value.header == pattern.0 && value.text.range(of: pattern.1, options: .regularExpression) != nil
}
But then Xcode (9) fails to compile with this error:
Tuple pattern cannot match values of the non-tuple type 'MyStruct'
The best I've been able to achieve is the following:
struct MatchMyStruct {
let header: String
let regex: String
init(_ header: NSString, _ regex: String) {
self.header = header
self.regex = regex
}
}
func ~=(pattern: MatchMyStruct, value: MyStruct) -> Bool {
return value.header == pattern.header && value.text.range(of: pattern.regex, options: .regularExpression) != nil
}
This lets me pattern match like this:
switch(someInstance) {
case MatchMyStruct("h1", "[a-z]+"): ...
case MatchMyStruct("h1", "0-9+"): ...
}
While this is functional, I'd much prefer not to have to have the MatchMyStruct
wrappers explicit like that.
It seems like swift has some magic secret sauce for pattern matching with tuples that's getting in the way. Is there anything I can do here?
You could make a computed property to return a tuple:
struct MyStruct {
let header: String
let text: String
var tuple: (String, String) { return (header, text) }
}
And then you can switch
based upon tuple
computed property:
switch(someInstance.tuple) {
case ("h1", "[a-z]+"):
...
case ("h1", "0-9+"):
...
default:
...
}
Or, if your intent was to perform regular expression matching:
switch(someInstance.tuple) {
case ("h1", let string) where string.range(of: "^[a-z]+$", options: .regularExpression) != nil:
print("alphabetic")
case ("h1", let string) where string.range(of: "^[0-9]+$", options: .regularExpression) != nil:
print("numeric")
default:
print("other")
}
Or, if that's too much of a mouthful, you can define some string functions for regex pattern matching, e.g.:
extension String {
func isMatch(regex pattern: String) -> Bool {
return range(of: "^" + pattern + "$", options: .regularExpression) != nil
}
func contains(regex pattern: String) -> Bool {
return range(of: pattern, options: .regularExpression) != nil
}
}
And then:
switch(someInstance.tuple) {
case ("h1", let string) where string.isMatch(regex: "[a-z]+"):
print("alphabetic")
case ("h1", let string) where string.isMatch(regex: "[0-9]+"):
print("numeric")
default:
print("other")
}
Or do this anyway you want, but it's just to illustrate that if you want tuple matching, you can just define computed property to return the tuple and then do whatever you want in the where
clauses.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With