Swift: cannot convert value of type to @noescape while call indexOf, after filter using filter function

11,186

The indexOf method on Swift arrays does not take an object of a type matching the array's type. Instead, it takes a closure. That closure takes an element of the array's type and returns a bool.

So, in fact, we don't (and shouldn't) even bother with the filter call unless we actually need the resultant array. If we're just looking for the first object that passes whatever test you are filtering for... well we just pass that exact same test to indexOf.

So, to keep things simple, if we have an array of strings (and let's say they're all single letter strings with lots of repetition), and I want to find the first index of the string "a", rather than filtering the array down to strings that are "a", then finding the first string that passed that test with the first method, and then finding the index of that exact object, instead, I just pass that test into the indexOf method:

let letters: [String] = ["x", "y", "z", "a", "b", "c"]

let index = letters.indexOf {
    $0 == "a"
}

For clarity, it appears that simply passing an individual element and looking for that does work in some cases. It probably relies on conformance to Swift's Equatable protocol. I could for example have simplied used letters.indexOf("a") here. The compiler would have been happy. But obviously, not every array is composed required to hold things that conform to Equatable, and the array can't make assumptions about how to compare its elements then. In these cases, you will have to use the above example of passing a closure. It's probably worth noting that passing this closure to indexOf rather than first filtering and then calling indexOf is going to be egregiously more efficient anyway, even if your array allows the letters.indexOf("a") approach. If for example, I had more complex strings, and I just wanted the first string that started with the letter 'a', this would be far, far more efficient than starting by filtering down the original array to an array of strings starting with 'a'.

Share:
11,186

Related videos on Youtube

Dima Deplov
Author by

Dima Deplov

Co-founder of pleeq.com and all related projects, including macilove.com blog with self made blog platform engine. Get in touch with me on Twitter @dimadeplov

Updated on June 04, 2022

Comments

  • Dima Deplov
    Dima Deplov almost 2 years

    In Swift 2, I'm receiving an error:

    Cannot convert value of type '[String:AnyObject]' to expected argument type '@noescape ([String:AnyObject])' throws -> Bool"

    //today = NSDate()
    //array : [[String:AnyObject]]
    // I use if let because of we might now get element in the array matching our condition
    
    if let elementOfArray = array.filter({$0["id"] as? Int == anotherDictionary["matchID"] as? Int && ($0["nextFireDate"] as? NSDate)?.compare(today) == NSComparisonResult.OrderedAscending}).first {
    
       let index = array.indexOf(elementOfArray) // error here
    }
    

    What I'm doing wrong? I can't understand. :/

    My aim, is to find index of that item, I think that I open for alternative solutions, but of course this one is preferred, because I think this is the "right way".

  • Dima Deplov
    Dima Deplov over 8 years
    almost! Oh, looks like this is the answer.
  • Martin R
    Martin R over 8 years
    There are two indexOf methods, one taking an element and one taking a predicate. ["x", "y", "z", "a", "b", "c"].indexOf("a") compiles without problems.
  • Dima Deplov
    Dima Deplov over 8 years
    @MartinR, but this not a dictionary, at least.
  • nhgrif
    nhgrif over 8 years
    @MartinR Indeed. I added a big footnote there. I'm fairly confident the "simple" one is only available for Equatable types (which explains why flinth couldn't see it probably). But additionally, as I explained, there will be cases where you don't want to be ignorant of the predicate overload even for Equatable types.
  • Martin R
    Martin R over 8 years
    @nhgrif: Yes, saw your addendum now.
  • Dima Deplov
    Dima Deplov over 8 years
    @nhgrif you were totally right, don't need filter at all. Just need only indexOf. Thanks, a lot!