簡體   English   中英

如何比較Swift中的Equatable

[英]How to compare Equatable in Swift

我有兩組這樣的兩個數組。 我從第三方獲得他們。

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。 但是,我不確定如何在這種情況下使用它。

基於(嘗試)類型轉換為已知元素類型構造簡單的逐元素比較函數

由於您的目標是比較(可選) 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
    }
}

或者,通過使用@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 #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 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的類型是一個Int ,並且索引1處的類型是String等...

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