How to Compare Equatable in Swift

I have several sets of two such arrays. I get them from a third party.

var array1 : [Any?]
var array2 : [Any?]

I know about the types of objects in this array (at compile time). As an example, the first element will be String, and the second - Int.

I am currently comparing each set of such arrays (note that arrays are not homogeneous).

array1[0] as? String == array2[0] as? String
array1[1] as? Int == array2[1] as? Int
...

The biggest problem is that each set has different types in it. As a result, I allowed 10 sets of arrays with 5 elements in each. I had to do a 10 * 5 explicit conversion to a specific type and comparison.

I want to be able to write a generic method that can compare two arrays (without having to specify all types)

compareFunc(array1, array2)

. , , . Equatable. , . , - .

func compare(array1: [Any?], array2: [Any?]) {
    for index in 0..<array1.count {
       if (array1[index] as? Equatable != array2[index] as? Equatable) {
         // Do something
       }
    }
}

Equableable. , .

+4
3

, ()

() Any , , switch, " ". , , ( ), , .

:

func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {
    /* element-by-element downcasting (to known types) followed by comparison */
    return arr1.count == arr2.count && !zip(arr1, arr2).contains {

        /* note that a 'true' below indicates the arrays differ (i.e., 'false' w.r.t. array equality) */
        if let v1 = $1 {

            /* type check for known types */
            switch $0 {
            case .None: return true
            case let v0 as String:
                if let v1 = v1 as? String { return !(v0 == v1) }; return true
            case let v0 as Int:
                if let v1 = v1 as? Int { return !(v0 == v1) }; return true
            /* ...
               expand with the known possible types of your array elements
               ... */
            case _ : return true
                /*  */
            }
        }
        else if let _ = $0 { return true }
        return false
    }
}

, , switch , ( ) compare(...) @Roman Sausarnes: s

func compareAnyArrays(arr1: [Any?], _ arr2: [Any?]) -> Bool {

    /* modified helper function from @Roman Sausarnes:s answer */
    func compare<T: Equatable>(obj1: T, _ obj2: Any) -> Bool {
        return obj1 == obj2 as? T
    }

    /* element-by-element downcasting (to known types) followed by comparison */
    return arr1.count == arr2.count && !zip(arr1, arr2).contains {

        /* note also that a 'true' below indicates the arrays differ
         (=> false w.r.t. equality) */
        if let v1 = $1 {

            /* type check for known types */
            switch $0 {
            case .None: return true
            case let v0 as String: return !compare(v0, v1)
            case let v0 as Int: return !compare(v0, v1)
                /* ...
                 expand with the known possible types of your array elements
                 ... */
            case _ : return true
                /*  */
            }
        }
        else if let _ = $0 { return true }
        return false
    }
}

:

/* Example usage #1 */
let ex1_arr1 : [Any?] = ["foo", nil, 3, "bar"]
let ex1_arr2 : [Any?] = ["foo", nil, 3, "bar"]
compareAnyArrays(ex1_arr1, ex1_arr2) // true

/* Example usage #2 */
let ex2_arr1 : [Any?] = ["foo", nil, 2, "bar"]
let ex2_arr2 : [Any?] = ["foo", 3, 2, "bar"]
compareAnyArrays(ex2_arr1, ex2_arr2) // false
+2

, , :

func compareAnyArray(a1: [Any?], _ a2: [Any?]) -> Bool {

    // A helper function for casting and comparing.
    func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
        return obj1 as? T == obj2 as? T
    }

    guard a1.count == a2.count else { return false }

    return a1.indices.reduce(true) {

        guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }

        switch $1 {
        // Add a case statement for each index in the array:
        case 0:
            return $0 && compare(_a1, _a2, t: Int.self)
        case 1:
            return $0 && compare(_a1, _a2, t: String.self)
        default: 
            return false
        }
    }
}

, , , [Any?], , 0 a Int, 1 - String ..

+1

The compact and Swift 5 version of Aaron Rasmussen's excellent solution :

func compare(a1: [Any?], a2: [Any?]) -> Bool {
    guard a1.count == a2.count else { return false }

    func compare<T: Equatable>(obj1: Any, _ obj2: Any, t: T.Type) -> Bool {
        return obj1 as? T == obj2 as? T
    }

    return a1.indices.reduce(true) {

        guard let _a1 = a1[$1], let _a2 = a2[$1] else { return $0 && a1[$1] == nil && a2[$1] == nil }

        switch $1 {
        case 0:  return $0 && compare(obj1: _a1, _a2, t: Int.self)
        case 1:  return $0 && compare(obj1: _a1, _a2, t: String.self)
        case 2:  return $0 && compare(obj1: _a1, _a2, t: <#TypeOfObjectAtIndex2#>.self)
        default: return false
        }
    }
}
+1
source

All Articles