dperk dperk - 5 months ago 28
Swift Question

Find index of dict in array of dict

Why can't I find the index of a dictionary in an array of dictionaries? I know each dictionary's key-value pair is unordered, but the order of dictionaries within the array should be ordered, and I should therefore be able to find the index.

For example:

var dict = [String:AnyObject]()
var array = [[String:AnyObject]]()

dict["name"] = "John" as? AnyObject
dict["city"] = "New York" as? AnyObject
array.append(dict)

for dict in array {

if let index = find(array, dict) {

// do something

}

}


I can't perform the function
find(array, dict)
.
Cannot invoke 'find' with an argument list of type '([([String:AnyObject])]), Dictionary<String,AnyObject>'

Answer

Problem

You want to use code like this:

if let index = array.indexOf(dict) {
    // do something
}

But it won't work. Why? Well, check out the definition of the method:

extension CollectionType where Generator.Element : Equatable {
    /// Returns the first index where `value` appears in `self` or `nil` if
    /// `value` is not found.
    ///
    /// - Complexity: O(`self.count`).
    @warn_unused_result
    public func indexOf(element: Self.Generator.Element) -> Self.Index?
}

This method will only work if the array's elements are Equatable. But Dictionarys aren't Equatable.

Even if your find() method doesn't work exactly this way, it must at some point rely on Equatable, which is why your code is failing.

Solutions

You could declare a custom struct and use that, which is preferable for a number of reasons:

struct Person : Equatable {
    let name : String
    let city : String
}

func ==(lhs: Person, rhs: Person) -> Bool {
    return true
}

let john = Person(name: "John", city: "New York")
let jane = Person(name: "Jane", city: "Boston")

var array = [john, jane]

for person in array {
    if let index = array.indexOf(person) {
        // do something with index
    }
}

For a quick-and-dirty solution, just call enumerate(), which includes the index of each element in a tuple:

var dict = [String:AnyObject]()
var array = [[String:AnyObject]]()

dict["name"] = "John"
dict["city"] = "New York"
array.append(dict)

for (index, element) in array.enumerate() {
    // do something with index
}