user1007522
user1007522

Reputation: 8118

Find item of specific type in array

I want an extension on a Array where you can find an item that is of some type.

I tried like this:

  func findItem<U: Type>(itemToFind: U) -> AnyObject? {
    for item in self {
        if let obj = item as? itemToFind {
            return obj
        }
    }
    return nil
}

So I check if it is the same type and then I want to return the obj.

The error I get is:

Inheritance from non-protocol, non-class 'Type'.

How can I fix this that I can pass to the function ViewController.self and that I get back nil if not found or the viewcontroller that is in the array?

Upvotes: 5

Views: 21915

Answers (3)

Kane Buckthorpe
Kane Buckthorpe

Reputation: 105

The compactMap is used to cast elements to the requested type and filter out any that fail.

You can then return the first item from the new collection.

When lazy is used, the values in the sequence or collection are produced on demand rather than being stored in an array. As a side effect you no longer need to cast after the filter.

extension Collection {
    public func first<T>(ofType _: T.Type) -> T? {
        let filteredItems = lazy.compactMap { $0 as? T }
        return filteredItems.first
    }
}

Upvotes: 0

mfaani
mfaani

Reputation: 36287

use is instead.

let arr : [Any] = ["jj", "j", 1, 1.0]

func findItems(of : Any.Type, from array: [Any]) -> Any? {
    for item in array { 
        if item is String {
            return item
        }
    }
    return nil
}

print(findItems(of: String.self, from: arr)) // jj

Upvotes: 0

Hamish
Hamish

Reputation: 80811

The syntax <U: Type> in this context means you're declaring a new generic placeholder U in your function signature, which inherits from (in the case of classes), or conforms to (in the case of protocols) Type.

As Type is neither a protocol nor class, I assume what you really want is an unconstrained generic placeholder, and instead want to pass in a given type as an argument to the function, which will define the type of U.

In this case, you'll want to use the metatype U.Type as the function input type (as well as U? for the function return type – as the return type will be the optional type of whatever type you pass into the function). For example:

extension Array {
    func firstElement<U>(ofType _: U.Type) -> U? {
        for element in self {
            if let element = element as? U {
                return element
            }
        }
        return nil
    }
}

let array : [Any] = [2, "bar", 3, "foo"]
print(array.firstElement(ofType: String.self)) // Optional("bar")

As a side note, this could be simplified slightly by using pattern matching in the for loop:

func firstElement<U>(ofType _: U.Type) -> U? {
    for case let element as U in self {
        return element
    }
    return nil
}

Upvotes: 20

Related Questions