Swift: assign to variable in switch case-let-where

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)

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.

Swift's pattern-matching switch statement � Episteme and Techne, Swift's switch statement can match not only numbers, but any equatable type ( such as strings):. let stateCode = "CA" switch stateCode { case� You use variables and constants in Swift to store information. It’s that simple! Variables are the “things” in your code, like numbers, text, buttons and images. Every bit of information your app uses, is stored in a variable or a constant. Knowing how variables work is the first step of learning iOS development. In this tutorial you’ll

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)
    }
}

Pattern Matching with case let, Swift has a particular keyword for applying Pattern Matching: case let. case loading case loaded(Data) case failed(Error) } switch state { case .loading: to match it to some pattern and assign associated value to a variable. To discover the specific type of a constant or variable that is known only to be of type Any or AnyObject, you can use an is or as pattern in a switch statement’s cases. The example below iterates over the items in the things array and queries the type of each item with a switch statement.

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 {

}

Patterns — The Swift Programming Language (Swift 5.3), The first kind of pattern is used for destructuring values in simple variable, You use these patterns in a case label of a switch statement, a catch clause of a do In the example above, let distributes to each identifier pattern in the tuple pattern � Swift also provides a for-in loop that makes it easy to iterate over arrays, dictionaries, ranges, strings, and other sequences. Swift’s switch statement is considerably more powerful than its counterpart in many C-like languages. Cases can match many different patterns, including interval matches, tuples, and casts to a specific type.

Pattern Matching, Part 4: if case, guard case, for case – Crunchy , is strictly equivalent to writing switch y { case let x: … } default: () // do nothing, but this is mandatory as all switch in Swift must be exhaustive } The order of arguments (pattern vs. variable) in that syntax can be troubling. On the contrary, force unwrapping says This variable does have a value while you use it. Therefore, when you force unwrap a variable that is nil, your program will throw an unexpectedly found nil while unwrapping an optional exception and crash. Some of the techniques for conditional unwrapping are explained below: 1. If-statement

Switch Statements in Swift Explained – LearnAppMaking, In this tutorial you learn how to use the switch statement in Swift. With switch you can inspect a value and match it with a number of cases. Let's go! based on one variable that can contain a number of possible values. Declaring Variables. The following code shows an example of a variable declaration: You begin a variable declaration with the var keyword followed by the name of the variable. Next, you add a colon, followed by the variable type. Afterward, you can assign a value to the variable using the (=) assignment operator.

switch or if case …, switch testEnum { case let .A(retrieveValue) : doA default : break }. or if case .A(let Cannot assign to property: 'options' is a 'let' constant Given that Swift optionals mean you often need non-optional variables derived from the optional ones,� Here: In the "if let" statement, we assign a variable to the optional's value. We then test the enum type. We then test the enum type. Swift program that creates enum from rawValue enum Animal: Int { case Bird = 3 case Fish = 4 case Insect = 5 } // Create Animal enum from a rawValue Int of 4.

Comments
  • example for fullline?
  • can't you just write: case let path.hasPrefix("Monet") and omit there where clause
  • how is it different from your first case?
  • @ato3787045 You're giving that closure where a boolean is expected. Perhaps you meant to call the closure by using ()?
  • Ahhh, i'm not a fan of that. That's a pretty unintuitive side effect. The last code snippet is what I would prefer, though
  • @Alexander, indeed. I never use it in my apps, just a code play.
  • Thank you, OOPer! Swift and Foundation are indeed expressive.
  • Thank you for the insight, Alexander! The Prefix/PredicateMatcher example you gave is a better solution. Looking at it again closely reveals the following subtlety when using () to invoke the closure at each case-let-where: as the switch statement sequentially executes each closure, the variable artist will always be assigned to the value of the last case! I am trying find a succinct syntax without using string literal twice. Is it possible to get at the case string(e.g. "Monet") within the case body?
  • let declarations within closures are local to the scope they're defined in, so that won't help you with creating an artist variable to use within the case body. Could you give me more of an explanation of what exactly you're trying to achieve (rather than how you're trying to achieve it?)
  • Alexander, I am trying to keep the string literal("Monet") close to the code fragment(case body) that uses it without duplicating the literal. So future developers can modify the literal at exactly one place. The case body code fragment will be different for different artists depending on the context provided by the rest of the fullline. Sometimes it will append additional string to the case value(e.g "Monet from 3rd Floor Collections", "Cezzane Tulips in a Vase 1890").
  • @ato3787045 " The case body code fragment will be different for different artists depending on the context provided by the rest of the fullline. " Can you elaborate on this? You're explaining what you're goals are with respect how you want to improve this existing code snippet, but you haven't explained to me what the problem is you're trying to solve. What is this code meant to do (without any regard for HOW it does it, I don't care about that)
  • Alexander, I added the context to the original question.