Hey there.
Simple question. Knowing the following, how would you find the first element of a given type, say Int
, without loosing the type info:
let values: [AnyHashable] = ["foo", 42, "bar"]
I see a couple approaches but I don't feel satisfied by either:
values.compactMap { $0 as? Int }.first // 1.
values.first(where: { $0 is Int }) as? Int // 2.
#1 feels like a waste because there are potentially many useless cast.
#2 is better but the type info is lost, so it requires to add the as? Int
.
I would have like to write values.first(as: Int.self)
.
Do you see a better way to do that?
Lantua
April 25, 2020, 2:32pm
2
Perhaps you could use lazy
values.lazy.compactMap { $0 as? Int }.first
4 Likes
You're right using lazy
is nice. And with a short extension
on Sequence
:
extension Sequence {
func first<T>(as: T.Type) -> T? {
lazy.compactMap { $0 as? T }.first
}
func last<T>(as: T.Type) -> T? {
lazy.compactMap { $0 as? T }.last
}
}
I can write values.first(as: Int.self)
which I think has the advantage of being concise while remaining clear.
Thanks @Lantua for the suggestion.
Syl20
(Sylvain Camus)
April 25, 2020, 6:59pm
4
I do not understand the interest of lazy
if it is to take the last matching item in the collection.
Lantua
April 25, 2020, 7:41pm
5
It's markedly less than first
, but you can still avoid creating intermediate array with it.
I've used nearly this exact extension for a while. It's really helpful with UIKit. e.g.
public extension UINib {
static func instantiate<Object: AnyObject>(owner: Any? = nil) -> Object? {
Bundle(for: Object.self)
.loadNibNamed("\(Object.self)", owner: owner)?
.getFirst()
}
}
However, it's not actually first
. It's getFirst
. first
is the result of getFirst
. And you shouldn't require an argument when you don't need it.
let array: [Any] = [1, "🥇"]
XCTAssertEqual(array.getFirst(), "🥇")
let getFirstInt = { array.getFirst(Int.self) }
XCTAssertEqual(getFirstInt(), 1)
public extension Sequence {
/// The first element of a given type.
func getFirst<T>(_: T.Type = T.self) -> T? {
lazy.compactMap { $0 as? T } .first
}
}