Test Enum for Equality in for ... Where Clause

Test enum for equality in for ... where clause

You can use if case:

for status in statuses {
if case .failed = status {
...
}
}

But, unfortunately, you can't use case with the where clause of for loop.


In this case, though, because you've defined .failed to be equal to another regardless of what the error associated value was, you theoretically could do:

for status in statuses where status == .failed(error: "") {
show("\(status)")
}

I'm not crazy about that pattern because (a) it's contingent upon the fact that .failed values are equal even if they have different error associated values; and (b) it results in code that is easily misunderstood.

How to test equality of Swift enums with associated values

Swift 4.1+

As @jedwidz has helpfully pointed out, from Swift 4.1 (due to SE-0185, Swift also supports synthesizing Equatable and Hashable for enums with associated values.

So if you're on Swift 4.1 or newer, the following will automatically synthesize the necessary methods such that XCTAssert(t1 == t2) works. The key is to add the Equatable protocol to your enum.

enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}
let t1 = SimpleToken.Number(123)
let t2 = SimpleToken.Number(123)

Before Swift 4.1

As others have noted, Swift doesn't synthesize the necessary equality operators automatically. Let me propose a cleaner (IMHO) implementation, though:

enum SimpleToken: Equatable {
case Name(String)
case Number(Int)
}

public func ==(lhs: SimpleToken, rhs: SimpleToken) -> Bool {
switch (lhs, rhs) {
case let (.Name(a), .Name(b)),
let (.Number(a), .Number(b)):
return a == b
default:
return false
}
}

It's far from ideal — there's a lot of repetition — but at least you don't need to do nested switches with if-statements inside.

How to compare enum with associated values by ignoring its associated value in Swift?

Edit: As Etan points out, you can omit the (_) wildcard match to use this more cleanly:

let number = CardRank.Number(5)
if case .Number = number {
// Is a number
} else {
// Something else
}

Unfortunately, I don't believe that there's an easier way than your switch approach in Swift 1.2.

In Swift 2, however, you can use the new if-case pattern match:

let number = CardRank.Number(5)
if case .Number(_) = number {
// Is a number
} else {
// Something else
}

If you're looking to avoid verbosity, you might consider adding an isNumber computed property to your enum that implements your switch statement.

How to check if at least one enum value is equal to a variable within an if condition

A few options:

  1. You can define your enums as flags. This means each value must be a power of 2 (1, 2, 4, 8, etc). Then, you could write:

if (randomValue & (Values.Value1 | Values.Value2) > 0)
{
//...
}

  1. You can use a switch

switch (randomValue)
{
case Values.Value1:
case Values.Value2:
{
//Do something
break;
}
case Values.Value3:
//Do something else
break;
default:
break;
}

  1. You can use an array (nicer if you have predefined sets of values you want to search for).

if (new[] { Values.Value1, Values.Value2 }.Contains(randomValue))
{
}

or

static(?) readonly Values[] allowedValues = new[] { Values.Value1, Values.Value2 };

void MethodName(Values randomValue)
{
if (allowedValues.Contains(randomValue))
{
//...
}
}

How to do an if-else comparison on enums with arguments

The trick is to not actually check with == but rather use the case keyword in conjunction with a single = in your if statement. This is a little counter intuitive in the beginning but just like if let, you get used to it pretty fast:

enum Normal {
case one
case two, three
}

enum NormalRaw: Int {
case one = 1
case two, three
}

enum NormalArg {
case one(Int)
case two, three
}

let normalOne = Normal.one
let normalRawOne = NormalRaw.one
let normalArgOne = NormalArg.one(1)

if case .one = normalOne {
print("A normal one") //prints "A normal one"
}

if case .one = normalRawOne {
print("A normal \(normalRawOne.rawValue)") //prints "A normal 1"
}

if case .one(let value) = normalArgOne {
print("A normal \(value)") //prints "A normal 1"
}

The point is that in Swift you only get equation of enums for free if your enum uses a raw type or if you have no associated values (try it out, you can't have both at the same time). Swift however does not know how to compare cases with associated values - I mean how could it? Let's look at this example:

Normal.one == .one //true
Normal.one == .two //false

NormalRaw.one == .one //true
NormalRaw.one == .two //false

NormalArg.one(1) == .one(1) //Well...?
NormalArg.one(2) == .one(1) //Well...?
NormalArg.one(1) == .two //Well...?

Maybe this makes it clearer why this cannot work out of the box:

class Special {
var name: String?
var special: Special?
}

enum SpecialEnum {
case one(Special)
case two
}

var special1 = Special()
special1.name = "Hello"

var special2 = Special()
special2.name = "World"
special2.special = special1

SpecialEnum.one(special1) == SpecialEnum.one(special2) //Well...?

So if you want enums with associated values, you'll have to implement Equatable protocol in your enum by yourself:

enum NormalArg: Equatable {
case one(Int)
case two

static func ==(lhs: NormalArg, rhs: NormalArg) -> Bool {
switch (lhs, rhs) {
case (let .one(a1), let .one(a2)):
return a1 == a2
case (.two,.two):
return true
default:
return false
}
}
}

Error why matching an enumeration using a if statement

Enums that don't have associated types are automatically equatable. Enums that have associated types aren't. This makes sense, because only you can know how the associated type (such as the integer that comes with your .un value) should be handled. Even though .trois doesn't have an associated type, the lack of freebie equateableness affects the whole enum. Switch works a little differently, using pattern matching, so it still works.

If you want your enum with an associated type to be equatable, you can define your own == operator:

enum SomeType {
case un(Int)
case deux
case trois
}

// possibly there's a more succinct way to do this switch
func ==(lhs: SomeType, rhs: SomeType) -> Bool {
switch (lhs,rhs) {
case let (.un(i), .un(j)) where i == j: return true
case (.deux,.deux): return true
case (.trois, .trois): return true
default: return false
}
}

var testValue: SomeType = .trois

if testValue == .trois {
println("equals .trois")
}

// note, for SomeType to work with generic
// functions that require Equatable, you have
// to add that too:
extension SomeType: Equatable { }

// which means this will work:
let a: [SomeType] = [.un(1), .deux, .trois]
find(a, .trois)

Check enum for multiple values

I ended up writing a method:

public static enum FileType {
CSV, XML, XLS, TXT, FIXED_LENGTH;

// Java < 8
public boolean in(FileType... fileTypes) {
for(FileType fileType : fileTypes) {
if(this == fileType) {
return true;
}
}

return false;
}

// Java 8
public boolean in(FileType... fileTypes) {
return Arrays.stream(fileTypes).anyMatch(fileType -> fileType == this);
}
}

And then:

if(fileType.in(FileType.CSV, FileType.TXT, FileType.FIXED_LENGTH)) {}

Nice and clean!

How to compare enum and int values?

It doesn't matter which you use, they will perform identically. If there is no enum for an integer value, .net creates one at runtime. There can be no exceptions.

However, Xichen Li is correct - why would you want to compare an enum against an integer value?

Can I use case enum comparison as a boolean expression?

Unfortunately, you cannot (directly) use a case condition as a Bool expression. They are only accessible in statements such as if, guard, while, for etc.

This is detailed in the language grammar, with:

case-condition → case ­pattern­ initializer­

This is then used in a condition:

condition → expression |­ availability-condition |­ case-condition­ | optional-binding-condition

(where expression represents a Bool expression)

This is then used in condition-list:

condition-list → condition­ | condition ­, ­condition-list
­

which is then used in statements such as if:

if-statement → if­ condition-list­ code-block­ else-clause­ opt­

So you can see that unfortunately case-condition is not an expression, rather just a special condition you can use in given statements.

To pack it into an expression, you'll either have to use an immediately-evaluated closure:

return { if case .active = session.state { return true }; return false }()

Or otherwise write convenience computed properties on the enum in order to get a Bool in order to check for a given case, as shown in this Q&A.

Both of which are quite unsatisfactory. This has been filed as an improvement request, but nothing has come of it yet (at the time of posting). Hopefully it's something that will be possible in a future version of the language.

C# Enum - How to Compare Value

use this

if (userProfile.AccountType == AccountType.Retailer)
{
...
}

If you want to get int from your AccountType enum and compare it (don't know why) do this:

if((int)userProfile.AccountType == 1)
{
...
}

Objet reference not set to an instance of an object exception is because your userProfile is null and you are getting property of null. Check in debug why it's not set.

EDIT (thanks to @Rik and @KonradMorawski) :

Maybe you can do some check before:

if(userProfile!=null)
{
}

or

if(userProfile==null)
{
throw new ArgumentNullException(nameof(userProfile)); // or any other exception
}


Related Topics



Leave a reply



Submit