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
}
]
排序闭包非常简单。
您比较两个对象:object1
和 object2
,其中 object1
是第一个参数闭包,object2
是第二个参数。这是 DataModel
个实例。
您可以通过返回 true
或 false
比较它们来决定这两个可以是数组中的任何对象,哪个应该首先出现。
在引擎盖下,它可以是任何排序方法(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 数组进行了排序。
我有一个名为 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
}
]
排序闭包非常简单。
您比较两个对象:object1
和 object2
,其中 object1
是第一个参数闭包,object2
是第二个参数。这是 DataModel
个实例。
您可以通过返回 true
或 false
比较它们来决定这两个可以是数组中的任何对象,哪个应该首先出现。
在引擎盖下,它可以是任何排序方法(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 数组进行了排序。