Is there a way to combine switch and contains?
Asked Answered
J

3

12

Let's assume that I have three sets containing extensions:

let photos: Set = ["jpg", "png", "tiff"]
let videos: Set = ["mp4", "mov", "mkv"]
let audios: Set = ["mp3", "wav", "wma"]

and a simple enum as:

enum FileType {
    case photo, video, audio, unknown
}

Now what I want to do is to implement a function that returns FileType option based on what is the passed string to it and which set contains it:

func getType(of file: String) -> FileType {
    if photos.contains(file) { return .photo }
    if videos.contains(file) { return .video }
    if audios.contains(file) { return .audio }

    return .unknown
}

It should work as expected, but I wonder if there is an approach to transform the if statement to one switch case (even if it would change the logic a bit), especially when working with enums the switch statement(s) is a better choice to avoid errors.

If it is unachievable by using switch statement, I would also appreciate any elegant alternative(s).

Junette answered 10/5, 2018 at 10:31 Comment(2)
Not really an answer to your specific question, but I would define a FileExtension type that has a fileType property with the corresponding file type, e.g gist.github.com/hamishknight/c211c63e321df33d18e9f90c72d4a20fEisteddfod
@Eisteddfod I completely agree with you. Also, some extension can be both "audio" and "video".Bywoods
B
11

I think your whole problem is the fact that you are trying to maintain 3 independent sets for every type instead of connecting them directly to a given file type:

enum FileType: String {
    case photo, video, audio, unknown
}

let extensions: [FileType: Set<String>] = [
    .photo: ["jpg", "png", "tiff"],
    .video: ["mp4", "mov", "mkv"],
    .audio: ["mp3", "wav", "wma"]
]

func getType(of file: String) -> FileType {
    return extensions.first { $0.value.contains(file) }?.key ?? .unknown
}
Bywoods answered 10/5, 2018 at 11:52 Comment(0)
T
5

You can add an overload to the pattern-matching operator ~=:

func ~= <T> (pattern: Set<T>, value: T) -> Bool {
    return pattern.contains(value)
}

func getType(of file: String) -> FileType {
    switch file {
    case photos: return .photo
    case videos: return .video
    case audios: return .audio
    default: return .unknown
    }
}
Trauma answered 10/5, 2018 at 12:25 Comment(0)
S
1

This works for your case

let photos: Set = ["jpg", "png", "tiff"]
let videos: Set = ["mp4", "mov", "mkv"]
let audios: Set = ["mp3", "wav", "wma"]

enum FileType {
    case photo, video, audio, unknown
}

func getType(of file: String) -> FileType {

   switch true {

   case photos.contains(file):
       return .photo
   case videos.contains(file):
       return .video
   case audios.contains(file):
       return .audio
   default:
       return .unknown
    }
}

print(getType(of: "mp4"))

video

You can switch on true and then use each case as the conditional when you have multiple conditions.

Shopkeeper answered 10/5, 2018 at 10:40 Comment(4)
Yeah it does, was the only way I could think of doing it with a switch statement. Do prefer your implementation thoughShopkeeper
I've never seen switching true before... how does this work?Ski
@Ski it just inverts the condition, used when there are multiple conditions. Just like saying when the case is trueShopkeeper
Makes sense, that's smart!Ski

© 2022 - 2024 — McMap. All rights reserved.