-
-
Save chockenberry/d6c08e9916442d11a0c69fb01454c063 to your computer and use it in GitHub Desktop.
| import UIKit | |
| enum Name { | |
| case foo(Foo) | |
| case bar(Bar) | |
| struct Foo { | |
| let name: String | |
| } | |
| struct Bar { | |
| let count: Int | |
| } | |
| } | |
| let names = [Name.foo(Name.Foo(name: "hello")), Name.bar(Name.Bar(count: 123)), Name.bar(Name.Bar(count: 999))] | |
| // The goal: to get the first Name.Bar item from names, and more importantly, to make the code readable. | |
| func firstBar_TakeOne(in names: [Name]) -> Name.Bar? { | |
| if case let .bar(bar) = names.first(where: { name in | |
| if case .bar(_) = name { | |
| return true | |
| } | |
| return false | |
| }) { | |
| return bar | |
| } | |
| return nil | |
| } | |
| func firstBar_TakeTwo(in names: [Name]) -> Name.Bar? { | |
| let initialResult: Name.Bar? = nil | |
| let result = names.reduce(into: initialResult) { result, name in | |
| guard result == nil else { return } | |
| if case let .bar(bar) = name { | |
| result = bar | |
| } | |
| } | |
| return result | |
| } | |
| if let bar = firstBar_TakeOne(in: names) { | |
| bar.count | |
| } | |
| if let bar = firstBar_TakeTwo(in: names) { | |
| bar.count | |
| } |
I would tend to reach for something like this:
let firstBar = names
.lazy
.compactMap({
switch $0 {
case let .bar(bar): bar
default: nil
}
})
.first;The combination of .lazy and .first should mean you only iterate as far through the collection as you need to, and the result is an Optional<Name.Bar>.
If you use the asBar or barValue helper values others have contributed above, you could then write it as:
return names.lazy.compactMap(\.asBar).firstand still get nearly optimal performance. A regular for loop will probably be marginally faster for small collections though but not by much.
Watch out, the performance of that construct may surprise you: https://forums.swift.org/t/adding-firstas-to-sequence/36665/17
Ahhhh, overloads fun. π The first(where: { _ in true }) hack is terrible, too π but if abstracted over and provided with a very good explanatory comment, I guess I could live with it. π
Building on the for-loop solution from @ezfe: you can make the variable binding in the loop condition:
This will iterate over
namesuntil it finds the first.bar(_)value and return its associated value (bound tobarin the loop condition). Ifnamesdoesn't contain any.bar(_)values it will return nil.