如何比较Swift中的Equatable

时间:2016-05-19 15:39:09

标签: arrays swift equatable

我有两组这样的两个数组。我从第三方那里得到它们。

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

我知道这些数组中的对象类型(在编译时)。例如,第一个元素是String,第二个元素是Int

我目前比较每组数组(请注意数组不是同质的)。

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

每个集合中存在不同类型的最大问题。结果,我想说10组数组,每组有5个元素。我必须做10 * 5显式转换为特定类型和比较。

我希望能够编写一个可以比较两个数组的公共方法(无需指定所有类型)

compareFunc(array1, array2)

我开始走在路上。但是,我无法弄清楚我应该投射什么物品。我试过了Equatable。但是,swift并不喜欢直接使用它。所以,这样的事情不起作用

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

此数组中的每个对象都是Equatable。但是,我不确定如何在这种情况下使用它。

3 个答案:

答案 0 :(得分:2)

构建一个基于(尝试)类型转换为已知元素类型的简单逐个元素比较函数

由于您的目标是比较(可选)class Market extends Eloquent { public function stores() { return $this->belongsToMany('Store'); } } 元素的数组,您可以构建一个函数,通过使用Any块来尝试逐个元素进行比较将数组的元素向下转换为&#34;第三方数组&#34; 中的不同的已知类型。请注意,您无需指定与特定类型相对应的元素位置(因为这可能在不同的数组之间有所不同),只需指定任何给定元素可能具有的不同类型的详尽集合。

这种功能的一个例子如下:

switch

或者替代方案,通过使用compare(...) helper function from @Roman Sausarnes:s answer

(稍微修改一下)使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

使用示例:

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

答案 1 :(得分:1)

这是一个边缘解决方案,但它应该减少您试图避免的一些重复代码:

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?],只要您知道索引处的类型0Int,索引1的类型是String等......

答案 2 :(得分:1)

紧凑型和 Swift 5 版本的 Aaron Rasmussen 解决方案:

Set<String> lst = itemLists.stream()
        .flatMap(x -> x.stream())
        .collect(Collectors.toSet());