Swift - 根据可选键的存在和键的非空字符串值对列表进行排序

Swift - Sorting list by the existence of an optional key AND non empty string value of the key

我有一个名为 DataModel 的模型,2 个具有字符串值的属性。该型号的列表如下所示。

struct DataModel: Hashable, Codable {
    var value: String
    var optionalKey: String?
}

[
 {
   value: "100",
   optionalKey: "value"
 },
 {
   value: "200"
 },
 {
   value: "300",
   optionalKey: ""
 },
 {
   value: "400",
   optionalKey: "value"
 }
]

如何安全地对列表进行排序,使 'optionalKey' 所在的任何对象都位于列表的开头。想要的结果如下:


[
 {
   value: "100",
   optionalKey: "value"
 },
 {
   value: "400",
   optionalKey: "value"
 }
 {
   value: "200"
 },
 {
   value: "300",
   optionalKey: ""  // <- empty string to be treated as if key does not exist

 }
]

排序闭包非常简单。
您比较两个对象:object1object2,其中 object1 是第一个参数闭包,object2 是第二个参数。这是 DataModel 个实例。
您可以通过返回 truefalse 比较它们来决定这两个可以是数组中的任何对象,哪个应该首先出现。
在引擎盖下,它可以是任何排序方法(bubble, etc.),这无关紧要。您只需比较两个元素,然后决定哪个元素应该在另一个元素之前。

这就是您应该能够编写的代码类型:

let explicit = models.sorted { object1, object2 in
    // They both have optionalKey value
    if let optional1 = object1.optionalKey, let optional2 = object2.optionalKey {
        if optional1.isEmpty && !optional2.isEmpty { //One if empty -> The other one should go first
            return false
        } else if !optional1.isEmpty && optional2.isEmpty { //One if empty -> The other one should go first (the other version)
            return true
        } else { //They are both empties OR both non-empty, we compare then just with value
            return object1.value < object2.value
        }
    } else if let optional1 = object1.optionalKey { //Here, only object2 had nil optionalKey value
        if optional1.isEmpty { //optionKey of object1 exists, but is empty
            return false
        } else { //optionKey of object1 exists, and is not empty
            return true
        }
    } else if let optional2 = object2.optionalKey { //Here, only object2 had nil optionalKey value
        if optional2.isEmpty { //optionKey of object1 exists, but is empty
            return true
        } else { //optionKey of object2 exists, and is not empty
            return false
        }
    } else { //Both are nil
        return object1.value < object2.value
    }
}
print(explicit)

它很冗长,但它是个例,现在我们可以简化它:

let sorted = models.sorted { object1, object2 in
    //Since in your case being nil or empty is the same, let's simplify with giving empty value directly
    let optionalValue1 = object1.optionalKey ?? ""
    let optionalValue2 = object2.optionalKey ?? ""
    if optionalValue1.isEmpty && !optionalValue2.isEmpty {
        return false
    } else if !optionalValue1.isEmpty && optionalValue2.isEmpty {
        return true
    } else {
        return object1.value < object2.value
    }
}
print(sorted)

可能会有更不冗长的代码,但由于您甚至不能对它进行冗长的排序,所以我会尽量“简化”它。您仍然需要了解它以便将来调试 it/change。

现在,我稍后再说,但是您可能不会那样比较字符串值,因为它们是“数字”。例如,如果您有 "2""10",在字符串比较中,"10" 低于 "2",因此您需要将 object1.value < object2.value 替换为object1.value.compare(object2.value, options: .numeric) != .orderedDescending

如果您正在反序列化 JSON 并希望空字符串为 nil,您可以实现自定义 init 函数:

extension DataModel {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        value = try values.decode(String.self, forKey: .value)
        let optionalKey = try? values.decode(String.self, forKey: .optionalKey)
        // If `optionalKey` string has no characters assign nil
        self.optionalKey = optionalKey?.count == 0 ? nil : optionalKey
    }
}

然后根据 optionalKey 值的存在对列表进行排序:

let list: [DataModel] = decodedData.sorted(by: { lhs, rhs in
    // `true` if left side is not `nil` and right side is `nil`
    // in other words, non-nil gets priority in the list 
    return lhs.optionalKey != nil && rhs.optionalKey == nil
})

如果您想根据 optionalKey 状态和 value 对 DataModel 对象数组进行排序,您应该查看下面的代码。

数组元素有optionalKey先按值排序再包含其他数组元素排序

private func sortDataObjectByValue() -> [DataModel]{
    let models = [
        DataModel(value: "100", optionalKey: "value"),
        DataModel(value: "800", optionalKey: ""),
        DataModel(value: "1800", optionalKey: "value"),
        DataModel(value: "1200"),
        DataModel(value: "900", optionalKey: ""),
        DataModel(value: "500", optionalKey: "value")
    ]
    
    var sortedModels:[DataModel] = []
    var modelsWithKey:[DataModel] = []
    var modelsWithoutKey:[DataModel] = []
    
    for model in models {
        if let key = model.optionalKey, key != "" {
            modelsWithKey.append(model)
        }
        else {
            modelsWithoutKey.append(model)
        }
    }
    
    modelsWithKey = modelsWithKey.sorted(by: { (Int([=10=].value) ?? 0) < (Int(.value) ?? 0) } )
    sortedModels += modelsWithKey
    
    modelsWithoutKey = modelsWithoutKey.sorted(by: { (Int([=10=].value) ?? 0) < (Int(.value) ?? 0) } )
    sortedModels += modelsWithoutKey
    
    debugPrint(sortedModels)
    return sortedModels
}

0 : DataModel - value : "100" optionalKey : Optional - some : "value"

1 : DataModel - value : "500" optionalKey : Optional - some : "value"

2 : DataModel - value : "1800" optionalKey : Optional - some : "value"

3 : DataModel - value : "800" optionalKey : Optional - some : ""

4 : DataModel - value : "900" optionalKey : Optional - some : ""

5 : DataModel - value : "1200" - optionalKey : nil

现在您已经根据 optionalKey 状态和值对 DataModel 数组进行了排序。