Arrays 如何在Swift中比较均衡

Arrays 如何在Swift中比较均衡,arrays,swift,equatable,Arrays,Swift,Equatable,我有多组这样的两个数组。我是从第三方获得它们的 var array1 : [Any?] var array2 : [Any?] 我知道这些数组中的对象类型(在编译时)。例如,第一个元素是字符串,第二个元素是Int 我目前比较了每一组这样的数组(请注意数组不是同质的) 最大的问题是每一套都有不同的类型。因此,我假设10组数组,每个数组中有5个元素。我必须对特定类型进行10*5的显式转换和比较 我希望能够编写一个通用方法,可以比较两个这样的数组(无需指定所有类型) 我开始沿着这条路走。但是,我也不

我有多组这样的两个数组。我是从第三方获得它们的

var array1 : [Any?]
var array2 : [Any?]
我知道这些数组中的对象类型(在编译时)。例如,第一个元素是
字符串
,第二个元素是
Int

我目前比较了每一组这样的数组(请注意数组不是同质的)

最大的问题是每一套都有不同的类型。因此,我假设10组数组,每个数组中有5个元素。我必须对特定类型进行10*5的显式转换和比较

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

我开始沿着这条路走。但是,我也不知道我应该如何投射这些对象。我尝试了
equalable
。然而,斯威夫特不喜欢直接使用它。所以,像这样的事情是行不通的

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

对于0..中的索引,这是一个边际解决方案,但它应该减少一些您试图避免的重复代码:

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
        }
    }
}
func compareAnyArray(a1:[任何?],a2:[任何?])->Bool{
//用于强制转换和比较的辅助函数。
func比较(obj1:Any、J2:Any、t:t.Type)->Bool{
将obj1返回为?T==obj2返回为?T
}
guard a1.count==a2.count else{return false}
返回a1.索引.减少(真){
guard let _a1=a1[$1],let _a2=a2[$1]else{return$0&&a1[$1]==nil&&a2[$1]==nil}
转换$1{
//为数组中的每个索引添加case语句:
案例0:
返回$0&&compare(_a1,_a2,t:Int.self)
案例1:
返回$0&&compare(_a1,_a2,t:String.self)
违约:
返回错误
}
}
}
这不是最漂亮的解决方案,但它将减少您必须编写的代码量,并且它应该适用于任意两个
[any?]
,只要您知道索引
0
处的类型是
Int
,索引
1
处的类型是
字符串
,等等。

基于(尝试的)到已知元素类型的类型转换,构造一个简单的逐元素比较函数 由于您的目标是比较(可选)
任何
元素的数组,因此您可以通过使用
开关
块构建一个函数来执行元素对元素的比较,以尝试将数组中的元素向下转换为“第三方数组”中的不同已知类型。请注意,您无需指定与特定类型对应的元素位置(因为这可能在不同的数组集之间有所不同),只需指定任何给定元素可能具有的不同类型的详尽集合

该函数的示例如下所示:

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
    }
}
或者,也可以通过使用(稍微修改)的


亚伦·拉斯穆森(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
        }
    }
}
func比较(a1:[任何?],a2:[任何?])->Bool{
guard a1.count==a2.count else{return false}
func比较(obj1:Any、J2:Any、t:t.Type)->Bool{
将obj1返回为?T==obj2返回为?T
}
返回a1.索引.减少(真){
guard let _a1=a1[$1],let _a2=a2[$1]else{return$0&&a1[$1]==nil&&a2[$1]==nil}
转换$1{
案例0:返回$0并进行比较(obj1:_a1,_a2,t:Int.self)
案例1:返回$0&&compare(obj1:_a1,_a2,t:String.self)
案例2:返回$0&比较(obj1:_a1,_a2,t:.self)
默认值:返回false
}
}
}

我想你应该退后一步,考虑一下你是如何结束一个<代码> > < <代码> >的。你说你是从第三方库中得到的,但是数组实际上代表了什么,为什么你需要比较它?你不能在纯SWIFT中做你要做的事情。(至少在没有字体铸造的情况下是这样,我想这会破坏你想要的东西的初衷),因为
=
运算符需要一个具体的类型才能进行任何比较。因此,由于Swift中的静态类型,试图抽象掉这些类型根本不起作用。这是最有希望的方法。它仍然需要在一个开关中指定所有类型,但类型比数组中的数组*索引集少。@VictorRonin我相信很难绕过这个要求,所以您可能不得不使用
switch
语句,其中明确列出了各种类型。可能可以使用一些运行时内省技巧来规避,但这不是我推荐的生产代码。请注意,您可能可以将我的Roman Sausarne:如果您想使数组元素比较短路(如果
false
element comp.);在我上面的回答中,在总结相等条件之前,将逐个元素对数组进行完全比较。实际上,运行时自省并不是一个坏主意。有一个库EVReflection,它在序列化/反序列化JSON方面做了大量工作,工作非常顺利(而且可靠)我认为这是一个有趣的方向,dfri对其进行了改进。但是,您的方法对我来说并不适用,因为不同的数组对于不同的索引有不同的类型。我的意思是集合1将有Int,String,Int,而集合2将有String,String,Int。
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
    }
}
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
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
        }
    }
}