简体   繁体   English

如何比较Swift中的Equatable

[英]How to compare Equatable in Swift

I have multiple sets of two arrays like that. 我有两组这样的两个数组。 I am getting them from a 3rd party. 我从第三方获得他们。

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

I know about types of objects in these array (in compile time). 我知道这些数组中的对象类型(在编译时)。 As example that first element will be a String and second is an Int . 例如,第一个元素是String ,第二个是Int

I currently compare each set of arrays like that (please notice that arrays aren't homogeneous). 我目前比较每组数组(请注意数组不是同类的)。

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

The biggest problem that each set have different types in it. 每个集合中存在不同类型的最大问题。 As result, I have let say 10 sets of arrays with 5 elements in each. 结果,我想说10组数组,每组有5个元素。 I had to do 10*5 explicit conversion to specific type and comparation. 我不得不做10 * 5显式转换为特定类型和比较。

I want to be able to write a common method which can compare two arrays like that (without a need to specify all the types) 我希望能够编写一个可以比较两个数组的公共方法(无需指定所有类型)

compareFunc(array1, array2)

I started to go down the road. 我开始走下去了。 However, I can't figure out what should I cast the objects too. 但是,我无法弄清楚我应该如何施放物体。 I tried Equatable . 我试过Equatable However, swift doesn't like direct usage of it. 但是,swift并不喜欢直接使用它。 So, something like that doesn't work 所以,这样的事情不起作用

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

Each object in this array will be Equatable. 此数组中的每个对象都是Equatable。 However, I am not sure how to use it in this case. 但是,我不确定如何在这种情况下使用它。

Construct a simple element-by-element comparison function based on (attempted) type conversion to known element types 基于(尝试)类型转换为已知元素类型构造简单的逐元素比较函数

Since you're aiming to compare arrays of (optional) Any elements, you could just construct a function that performs element-by-element comparison by using a switch block to attempt to downcast the elements of the array to different known types in your "3rd party arrays" . 由于您的目标是比较(可选) Any元素的数组,您可以构建一个函数,通过使用switch块尝试将数组的元素向下转换为“ 不同的已知类型”来执行逐元素比较。 第三方阵列“ Note that you needn't specify which element position that corresponds to a specific type (as this might differ between different set of arrays), just the exhaustive set of the different types that any given element might be of. 请注意,您无需指定与特定类型对应的元素位置(因为这可能在不同的数组集之间有所不同),只需指定任何给定元素可能具有的不同类型的详尽集合。

An example of such a function follows below: 这种功能的一个例子如下:

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
    }
}

or, alternative, making the switch block a little less bloated by making use of (a slightly modified of) the compare(...) helper function from @Roman Sausarnes:s answer 或者,通过使用@Roman Sausarnescompare(...)辅助函数 (略微修改)使switch块变得不那么臃肿:答案

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: 用法示例:

/* 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

This is a marginal solution, but it should reduce some of the duplicative code that you are trying to avoid: 这是一个边缘解决方案,但它应该减少您试图避免的一些重复代码:

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
        }
    }
}

That isn't the most beautiful solution, but it will reduce the amount of code you have to write, and it should work for any two [Any?] , as long as you know that the type at index 0 is an Int , and the type at index 1 is a String , etc... 这不是最美丽的解决方案,但它会减少你必须编写的代码量,它应该适用于任何两个[Any?] ,只要你知道索引0的类型是一个Int ,并且索引1处的类型是String等...

A compact and Swift 5 version of great solution of Aaron Rasmussen : Aaron Rasmussen的紧凑和Swift 5版本的优秀解决方案:

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
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM