Reputation: 313
my question might be simple, but it got me puzzled a bit:
Imagine I have an array of different objects which all have a common parent class "MyClass
".
var values = [MyClass]()
However they all have their specific subclass like for example "MyClassSubclass
".
values.append(MyClassSubclass())
I now want to create a generic method returning me the first object inside this array which is of type MyClassSubclass
. I would like to prevent casting the object, but instead have a generic method which takes the subclass object as T parameter and returns me the first occurrence of subclass T inside this array.
I thought of something like this (but surely that does not work):
func getFirst<T: MyClass>(_ ofType : T.Type) -> T?
I guess I'm just stuck and I don't know what to search for, so if someone could help me I would greatly appreciate it.
Edit Example based on the above values:
class MyClass {}
class MyClassSubclass : MyClass {}
class MyClassSubclass2 : MyClass{}
var values = [MyClass]()
values.append(MyClassSubclass())
values.append(MyClassSubclass2())
//Return the first class element appearing here as a subclass type
func getFirst<T>(_ ofType : T.Type) -> T?{}
Thanks
Upvotes: 3
Views: 813
Reputation: 539685
One approach is to iterate over the array and use optional binding to check if an element is of the given type:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for elem in values {
if let item = elem as? T {
return item
}
}
return nil
}
This can be simplified using for case
with the as
pattern:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
for case let item as T in values {
return item
}
return nil
}
Another approach is to use flatMap
to find all items of the given
type and return the first one:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.flatMap { $0 as? T }.first
}
If the array can be large and you want to avoid the creation of an
intermediate array then you can use lazy
:
func getFirst<T: MyClass>(ofType: T.Type) -> T? {
return values.lazy.flatMap { $0 as? T }.first
}
As an array extension method this would be
extension Array {
func getFirst<T>(ofType: T.Type) -> T? {
return flatMap { $0 as? T }.first
}
}
Upvotes: 7
Reputation: 181
If you'd like to use global function, which is not recommended, try this
func getFirst<T>(ofType: T.Type) -> T? where T: MyClass {
for value in values where value is T {
return value as? T
}
return nil
}
let first = getFirst(ofType: MyClassSubclass2.self)
The first answer should be better for Swift.
Upvotes: 2
Reputation: 7031
This feels a bit like abuse of generics, but here's an extension:
extension Array where Element == MyClass {
func getFirst<T>(_ ofType: T.Type) -> T? {
return self.first(where: { ofType == type(of: $0) }) as? T
}
}
The method can then be called as let first = values.getFirst(MyClassSubclass.self)
.
I'd personally prefer simply casting inline for clarity:
let first = values.first(where: { type(of: $0) == MyClassSubclass.self }) as? MyClassSubclass
Upvotes: 1