How to find an instance in an array of enums?

I can use the for case let in construct to match a particular case of enum in an array of them, however, that won't actually give me a reference to the enum instance. You have this...

for case let Action.interval(time, delta) in actions  {

} 

Unfortunately, that won't give me a reference to the Action.interval instance itself. The idea is to find the first .interval Action in the list and save a reference to it for later. Does anyone know how I can do this in just one statement? Is it even possible? It's easy to do with multiple statements of course, but I was hoping to make my code a little more concise. I tried zipping the actions with an index but can't get that to destructure with the case let in place.

Maybe you can do this:

let actions: [Action] = [lots of different actions]
let firstInterval = actions.first {
   if case .interval = $0 { 
     return true 
   }
   return false
}
1 Like

There isn't an elegant way to achieve this unfortunately, as pattern matching statements are not boolean expressions. What @suyashsrijan posted is the best you can get. You probably want to add a property like var isInterval: Bool so you can make the predicate more concise if you are doing this sort of filtering often.

Do you think i could get away with something like this...

let first = actions.first { $0.isCase == .interval }

or similar such as a function that takes .interval? Is that even possible? I was hoping to get away with just one piece of logic on my enum to handle case evaluation.

That idea is being discussed in this thread, and the examples there show how you can manually define your own nested enum without associated values to simplify this kind of thing.

1 Like

Maybe it's due to wording, but note that enum is a value type. It has no notion of reference.
If you create a new enum with the exact same value, for all intent and purpose, you'll get the exact same enum.

So nothing stops you from doing this

for case let Action.interval(time, delta) in actions  {
  let value = Action.interval(time, delta)
  // Indistinguishable from the actual value in `actions`
}

The slightly trickier part is when pattern doesn't match exactly back to a single value (common one is when the value is class-based equatable, and you want the identical class).

enum Test {
  case foo(Int), bar
}

let array = [Test.foo(4), .bar, .foo(2)]
for case .foo(0..<3) in array {
  ...
}

In which case you can use where clause

for case .foo(let x) in array where 0..<3 ~= x {
  ...
}

Should you really need the actual value, others have already suggested a few ways to do it.

Yes, its wording.

You'll get an enum with the same value, but surely you won't get the exact same enum? That's the whole point of value types.

There could be some detailed difference like storage, performance, COWing, etc, but semantically speaking, 2 enums that are created from indistinguishable values are indistinguishable (so there's no reason other than performance to be strict about it). Much like how every 3 as Int is the same.
Then again, technically that's not the exact same enum, pardon my bad wording :stuck_out_tongue:.

Semantically, they're exactly the same as far as the language model is concerned. So you could write:

for case let Action.interval(time, delta) in actions  {
  let interval = Action.interval(time.delta)
  ...
} 

to reconstruct the value. In principle, the optimizer hopefully would see that these are identical and fold the construction away. It'd be nice if the language had a way for binding subpatterns, though, like other languages with pattern matching do.

3 Likes