Reputation: 195
Is it possible to make an assignment to the artist variable before it is used in the where subclause?
var artist
switch fullline {
case let path where path.hasPrefix("Monet"):
artist = "Monet"
case let path where path.hasPrefix("Cezanne"):
artist = "Cezanne"
default: ()
}
Closure:
case let path where { () -> Bool in let artist = "Monet"; return path.hasPrefix(artist) }:
Error:
() -> Bool' is not convertible to 'Bool'
Context:
I have lines of freeform text with artist name as the prefix that requires massaging to output consistent humanly readable text. e.g.
Monet : Snow at Argenteuil 02, 1874 Monet - Snow at Argenteuil, 1874, 3rd Floor Collections Monet, Claude - 1875, Snow in Argenteuil Cezzane - Vase of Flowers, 1880-81, print Cezzane, Paul 1900-1903 Vase of Flowers Cezzane - Vase with Flowers, 1895-1896
There will be a code fragments that performs detailed processing/categorizing for each artist. Hence the processing logic is artist dependent.
I would like to define similar to the following construct
switch fullline
hasPrefix(artist = "Monet")
-> code logic 1
get_birthday(artist)
hasPrefix(artist = "Cezzane")
-> code logic 2
get_birthday(artist)
Upvotes: 4
Views: 18551
Reputation: 77616
You can achieve this by switching over a tuple of your enum and your optional. Optional is an enum too, so you can switch both of them
enum SomeSnum {
case a, b, c
}
let someString: String? = "something"
let esomeEnum = SomeSnum.b
switch(esomeEnum, someString) {
case (.b, .some(let unwrappedSomething)) where unwrappedSomething.hasPrefix("so"):
print("case .b, \(unwrappedSomething) is unwrapped, and it has `so` prefix")
case (.a, .none):
print("case .a, and optional is nil")
default:
print("Something else")
}
You can also do an if statement
if case let (.b, .some(unwrappedSomething)) = (esomeEnum, someString), unwrappedSomething.hasPrefix("so") {
} else if case (.a, .none) = (esomeEnum, someString) {
} else {
}
Upvotes: 1
Reputation: 47896
With a little modification to the Alexander's struct, you can write something like this:
struct PrefixMatcherWithHandler {
var handler: (String)->Void
var string: String
init(_ string: String, handler: @escaping (String)->Void) {
self.string = string
self.handler = handler
}
static func ~= (prefix: String, matcher: PrefixMatcherWithHandler) -> Bool {
if matcher.string.hasPrefix(prefix) {
matcher.handler(prefix)
return true
} else {
return false
}
}
}
var fullline: String = "Monet, Claude"
var artist: String? = nil
let matcher = PrefixMatcherWithHandler(fullline) {str in
artist = str
}
switch matcher {
case "Monet":
break
case "Cezanne":
break
default: break
}
print(artist ?? "") //->Monet
But having some side-effect in boolean operators like ~=
makes your code less readable and can easily make unexpected result.
If you just want to reduce some redundant reference to a same thing, switch
-statement may not be a good tool for it.
For example, you can get the same result without defining specific matcher types:
var fullline: String = "Monet, Claude"
var artist: String? = nil
if let match = ["Monet", "Cezanne"].first(where: {fullline.hasPrefix($0)}) {
artist = match
}
print(artist ?? "") //->Monet
ADDED for updated parts of the question
The following code behaves slightly different than prefix-matching, but I believe you do not want to match "Mon"
to the line Monet, Claude - 1875, Snow in Argenteuil
.
extension String {
var firstWord: String? {
var result: String? = nil
enumerateSubstrings(in: startIndex..<endIndex, options: .byWords) {str, _, _, stop in
result = str
stop = true
}
return result
}
}
func get_birthday(_ artist: String) {
//What do you want to do?
print(artist)
}
var fullline: String = "Monet, Claude - 1875, Snow in Argenteuil"
switch fullline.firstWord {
case let artist? where artist == "Monet":
//code dedicated for "Monet"
get_birthday(artist)
case let artist? where artist == "Cezanne":
//code dedicated for "Cezanne"
get_birthday(artist)
default:
break
}
When you can retrieve data suitable for switch
-statement, the code would be far more intuitive and readable.
Upvotes: 3
Reputation: 63281
You're giving that closure where a boolean is expected. Not sure why you would want to do this, but you could make it work by using ()
to invoke the closure.
var artist
switch fullline {
case let path where { () -> Bool in let artist = "Monet"; return path.hasPrefix(artist) }():
artist = "Monet"
case let path where path.hasPrefix("Cezanne"):
artist = "Cezanne"
default: ()
}
Here is how I would do this:
import Foundation
struct PrefixMatcher {
let string: String
init(_ string: String) { self.string = string }
static func ~= (prefix: String, matcher: PrefixMatcher) -> Bool {
return matcher.string.hasPrefix(prefix)
}
}
extension String {
var prefix: PrefixMatcher { return PrefixMatcher(self) }
}
let fullline = "Monet 123456789"
let artist: String?
switch fullline.prefix {
case "Monet": artist = "Monet"
case "Cezanne": artist = "Cezanne"
default: artist = nil
}
print(artist as Any)
More general solution:
import Foundation
struct PredicateMatcher<Pattern> {
typealias Predicate = (Pattern) -> Bool
let predicate: Predicate
static func ~=(pattern: Pattern,
matcher: PredicateMatcher<Pattern>) -> Bool {
return matcher.predicate(pattern)
}
}
extension String {
var prefix: PredicateMatcher<String> {
return PredicateMatcher(predicate: self.hasPrefix)
}
}
Upvotes: 2