Ive got an enum
enum Display {
case string(String)
case int(Int)
case blank
}
and want to check if an element in my [Display] is blank.
I can do this with a switch case
switch element {
case .blank:
print ("all is fine")
default:
return
}
so if it isn't blank we return.
I'd prefer an if statement
if element != blank
But I can't seem to do this with an Enum. Surely there's a better way?
You can use if case for that. Read here: http://fuckingifcaseletsyntax.com
Copy paste from the website for you lazy guys out there:
if case let Puppy.mastiff(droolRating, weight) = fido {
... // tell fido he's a good boy
is equivalent to:
switch fido {
case let Puppy.mastiff(droolRating, weight):
... // tell fido he's a good boy
If you are going to to this in several places in your code it might be worth adding a computed property to your enum to check .blank
var isBlank: Bool {
get {
if case Display.blank = self {
return true
}
return false
}
}
Then you can use it in a way that makes your other code cleaner
array.contains(where: {$0.isBlank})
element.isBlank
Related
enum ImportType: Int {
case First = 1
case None
case Original
}
var type: ImportType = .First
print(type) --------------------> This will output "First"
NSLog("%#", String(type) --------------------> I can't do this.
NSLog("%d", type.rawValue) --------------------> This will output "1"
Hi All,
I want to get similar result of NSLog as the print function, it more readable for people, but I can't found a way to do this, I got some result that need to do additional handling inside the enum, but I am using other body's source code and just want to collect some information directly.
Are there easy transform way to do what I want?
Thanks~~
Eric
print uses String.init(describing:) under the hood to convert whatever you give it to a String, so you can do that too:
NSLog("%#", String(describing: type))
But really though, the enum should conform to CustomStringConvertible:
enum ImportType: Int, CustomStringConvertible {
case First = 1
case None
case Original
var description: String {
switch self {
case .First:
return "First"
case .None:
return "None"
case .Original:
return "Original"
}
}
}
and you should not rely on this default behaviour of String(describing:), because its behaviour is not specified unless the type conforms to TextOutputStreamable, CustomStringConvertible or CustomDebugStringConvertible. See here for more info.
I have an enum similar to this, where all cases contain the same associated value content:
enum RowType {
case single(_ content: [Any])
case double(_ content: [Any])
case triple(_ content: [Any])
...
}
I know I could just use a struct with a rowType and a content attribute, but let's please not discuss this, but rather have a look at the following:
When I want to switch all the cases, I could of course do this:
switch row {
case .single(let content):
// do anything
break
case .double(let content):
// ...
...
}
or even:
switch row {
case .single(let content), .double(let content), ...:
// do the same thing for all cases
break
}
Now my enum contains a few more cases and is likely to grow further while under development, so it's inconvenient for me to list all cases in the same case statement just to unwrap the content argument.
So I got curious and wondered: Can I somehow "wildcard" the enum case itself and still unwrap the content field? Like a default case with associated value...
I was hoping to be able to do something like ...
switch row {
case _(let content):
// do something
break
}
... or maybe accessing the associated value in the default case.
I did a bit of research but couldn't find an answer, so I'm excited for your thoughts.
Try this in Playground.
enum RowType {
case single(_ content: [Any])
case double(_ content: [Any])
case triple(_ content: [Any])
case noVal
var associatedValue: Any? {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return associated.value
}
print("WARNING: Enum option of \(self) does not have an associated value")
return nil
}
}
}
let row : RowType = .double([1,2,3])
let rowNoVal : RowType = .noVal
row.associatedValue
rowNoVal.associatedValue
For demo purposes I've created next code:
enum WeatherType {
case cloudy(coverage: Int)
case sunny
case rainy
}
let today: WeatherType = .cloudy(coverage: 0)
switch today {
case .cloudy(let coverage) where coverage == 0, .sunny: // <-- This line doesn't compile
print("☀️")
case .cloudy(let coverage) where 1...100 ~= coverage:
print("☁️")
case .rainy:
print("🌧")
default:
print("Unknown weather")
}
The compile error message is 'coverage' must be bound in every pattern. As I've already googled that a way to go with associated values is to compare different states of values within the same enum case. But this may lead to code duplication, like in my example, where I need to write two case statements for .sunny and .cloudy(let coverage) where coverage == 0.
Is there any correct, swifty way, to deal with such cases?
You don't need a where-clause to match .cloudy(coverage: 0), just
case .cloudy(coverage: 0), .sunny:
print("☀️")
Another option is to use fallthrough, for example
case .cloudy(let coverage) where coverage < 10:
fallthrough
case .sunny:
print("☀️")
Martin is right (+1).
But I might suggest a further refinement, moving your switch into an an extension of WeatherType (which I’d probably just call Weather) so that you don’t have to repeat this switch statement every time you’re looking for the symbol:
enum Weather {
case cloudy(Int)
case sunny
case rainy
}
extension Weather {
var symbol: String {
switch self {
case .cloudy(0), .sunny:
return "☀️"
case .cloudy:
return "☁️"
case .rainy:
return "🌧"
}
}
}
Then you can do:
let today: Weather = .cloudy(0)
print(today.symbol)
And even if you wanted to consider anything less than 10 as sunny, you still don’t need where clause:
extension Weather {
var symbol: String {
switch self {
case .cloudy(..<10), .sunny:
return "☀️"
case .cloudy:
return "☁️"
case .rainy:
return "🌧"
}
}
}
Pattern matches are composable, so you can do this:
switch today {
case .sunny, .cloudy(0):
print("☀️")
case .cloudy(1...10):
print("☁️")
case .rainy:
print("🌧")
default:
print("Unknown weather")
}
This is the first time I use this kind of enums, enum with associated value type, I need to make a switch statement depending on the object's type, I cannot managed to do it, this is the enum:
enum TypeEnum {
case foo(FooClass)
case doo(DooClass)
case roo(RooClass)
}
My object has a variable of type TypeEnum, now I need to check which kind of object is in the enum:
if let anObject = object as? TypeEnum {
switch anObject {
case .foo(???):
return true
...
default:
return false
}
}
I have no idea what to put instead of ???. Xcode tells me to put something, but I just want to switch on .foo.
Any ideas?
You can use let to capture the associated values for that:
switch anObject {
case .foo(let fooObj):
...
}
or nothing at all if you just don't care about them:
switch anObject {
case .foo:
...
}
Please be sure to check the Swift Programming Language book for further details.
You could use the underscore to indicate that you aren't interested in the associated type:
case .foo(_):
...
I'm searching for the syntax to do pattern matching with multiple cases in an if case statement.
The example would be this:
enum Gender {
case Male, Female, Transgender
}
let a = Gender.Male
Now I want to check, if a is .Male OR .Female. But I would like to avoid using switch for this. However the switch statement would be like this:
switch a {
case .Male, .Female:
// do something
}
Is it possible to write this with if case?
I would expect this, but it didn't work :(
if case .Male, .Female = a {
}
A simple array does the trick:
if [.Male, .Female].contains(a) {
print("Male or female")
} else {
print("Transgender")
}
I'm simply amazed at Swift's ability to infer type. Here, it gets that .Male and .Female are of type gender from a.
If you have an associated value, you can simply create a Bool variable like this
extension Gender {
var isMaleOrFemale: Bool {
switch self {
case .Male, .Female:
return true
default:
return false
}
}
And usage:
if a.isMaleOrFemale {
// Your code here
}
You should use a collection. In JavaScript I would write something like this:
if ([Gender.Male, Gender.Female].includes(actualGender))
console.log(actualGender);
Note that I have not a clue about swift, or how to do the same in that language, so here is a relevant answer in the topic: https://stackoverflow.com/a/25391725/607033 :D
EDIT: This is the Swift version:
if [.Male, .Female].contains(a) {
}
For pattern matching, what you describe will not work yet. You could do this in your case. But if it cannot be convert into a hashValue. Then this would not work either.
// Using Pattern Matching for more than one case.
if case 0...2 = a.hashValue {
print("Hello")
}
//Normal if else
if a == .Male || a == .Female {
print("Hello")
}