Vincent Bernier
Vincent Bernier

Reputation: 8664

Swift Dictionary of string's array equality

I'm trying to have a dictionary that have Strings as key and array of String as value.
Then I want to check if 2 of those dictionaries are equal like so:

let dicOfStringStringAr1: [String : [String]] = ["key1" : ["val", "for key 1"]]
let dicOfStringStringAr2: [String : [String]] = ["key1" : ["val", "for key 1"]]
if dicOfStringStringAr1 == dicOfStringStringAr2 {
    print("Dictionary of String Array")
}

But I get this error:

error: binary operator '==' cannot be applied to two '[String : [String]]' operands

But if the values are String instead of [String] it works.

let dicOfStringAr1: [String : String] = ["key1" : "val"]
let dicOfStringAr2: [String : String] = ["key1" : "val"]
if dicOfStringAr1 == dicOfStringAr2 {
    print("Dictionary of String Array")
}

I don't understand the problem.
Someone can explain what is the issue.


After looking at the default == Swift function for Dictionary

@warn_unused_result
public func ==<Key : Equatable, Value : Equatable>(lhs: [Key : Value], rhs: [Key : Value]) -> Bool

I realized that there is no definition where Value is represented as an array, and that the "generic" Value can't be resolve to a collection. (Not sure why).
So I made a == function that would have [Key : [Value]] parameters. And now the code work.

@warn_unused_result
public func ==<Key : Equatable, Value : Equatable>(lhs: [Key : [Value]], rhs: [Key : [Value]]) -> Bool {
    var result = lhs.count == rhs.count
    for (k, v) in lhs {
        guard let arValue = rhs[k] where result == true else {
            result = false
            break
        }

        result = v == arValue
    }

    return result
}

Can someone explain why in the Swift == implementation Value can't be resolve to a single item, being an array?

Upvotes: 1

Views: 1331

Answers (2)

J.Wang
J.Wang

Reputation: 1236

It actually makes sense. As you can see from the == function for type <Key : Equatable, Value : Equatable>, it requires Value to conform Equatable. By default, Array type doesn't conform to Equatable. Note, this doesn't mean you cannot compare two arrays using ==.

[1, 2, 3] == ["a", "b", "c"] // false.

Conforming to Equatable means you can use == but not vice verse.

You can of course extend the Array type to conform to Equatable.

extension Array: Equatable {}
// Implement the following method.
public func ==<T: CollectionType, U: CollectionType>(lhs: T, rhs: U) -> Bool {}

Update

I would say it's not possible right now to implement a plausible == for Array. (It might be possible in the future swift version)

For your specific case, I would say the following method is fair enough. When you have a custom struct, just make sure it conforms Equatable.

public func  ==<Key : Equatable, Value : CollectionType where Value.Generator.Element: Equatable>(lhs: [Key : Value], rhs: [Key : Value]) -> Bool {
    guard lhs.keys.elementsEqual(rhs.keys) else { return false }

    for (key, value) in lhs {
        if !value.elementsEqual(rhs[key]!) { return false }
    }

    return true
}

Example would be:

struct Number: Equatable {
    var x: Int
    var y: Int
}

func ==(lhs: Number, rhs: Number) -> Bool {
    return lhs.x == rhs.x && lhs.y == rhs.y
}

let dicOfStringStringAr1: [String : [Number]] = ["key1" : [Number(x: 1, y: 2), Number(x: 1, y: 2)]]
let dicOfStringStringAr2: [String : [Number]] = ["key1" : [Number(x: 1, y: 2), Number(x: 1, y: 2)]]
if dicOfStringStringAr1 == dicOfStringStringAr2 {
    print("Dictionary of String Array") // "Dictionary of String Array"
}

Upvotes: 1

yesthisisjoe
yesthisisjoe

Reputation: 2035

Fortunately, you can check if these dictionaries are equal by converting your dictionary to a NSDictionary and using its method isEqualToDictionary. For example:

if NSDictionary(dictionary: dicOfStringStringAr1).isEqualToDictionary(dicOfStringStringAr2) {
    print("Dictionary of String Array")
}

Upvotes: 0

Related Questions