-
-
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 | |
} |
Building on the for-loop solution from @ezfe: you can make the variable binding in the loop condition:
func firstBar(in names: [Name]) -> Name.Bar? {
for case .bar(let bar) in names {
return bar
}
return nil
}
This will iterate over names
until it finds the first .bar(_)
value and return its associated value (bound to bar
in the loop condition). If names
doesn't contain any .bar(_)
values it will return nil.
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).first
and 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. 🙃
I would personally go with:
because I assume struct Name.Bar's role is nailed down enough; if it isn't, what Jared Sinclair said.