Swift 按键排序字典数组,其中值是可选的 AnyObject
Swift sort array of dictionaries by key where value is optional AnyObject
我直接从 Parse 中提取字典数组并将它们显示在 table 中。所以我真的很想使用我收到的数据结构(下面结构奇怪的字典)。
A PFObject
是 [String : AnyObject?]
并且我希望能够按任意键进行排序,因此我不知道对象类型并且某些词典中可能缺少该键。因为在 Parse 中,如果你不给 属性 一个值,它就根本不存在。例如:
[
{
"ObjectId" : "1",
"Name" : "Frank",
"Age" : 32
},
{
"ObjectId" : "2",
"Name" : "Bill"
},
{
"ObjectId" : "3",
"Age" : 18
}
{
"ObjectId" : "4",
"Name" : "Susan",
"Age" : 47
}
]
我希望缺少键的词典总是排在排序后的词典之后。一个例子:
原文Table:
ObjectId Name Age
1 Frank 32
2 Bill
3 18
4 Susan 47
按姓名排序:
ObjectId Name Age
2 Bill
1 Frank 32
4 Susan 47
3 18
由于我对数据模型没有太多控制权,而且它在整个应用程序中的使用受到限制,我更愿意专注于算法解决方案而不是结构化解决方案。
我想出了一个方法来做到这一点,但它似乎效率低下且缓慢,我确信有人可以做得更好。
//dataModel is an array of dictionary objects used as my table source
//sort mode is NSComparisonResult ascending or descending
//propertyName is the dictionary key
//first filter out any objects that dont have this key
let filteredFirstHalf = dataModel.filter({ [=14=][propertyName] != nil })
let filteredSecondHalf = dataModel.filter({ [=14=][propertyName] == nil })
//sort the dictionaries that have the key
let sortedAndFiltered = filteredFirstHalf { some1, some2 in
if let one = some1[propertyName] as? NSDate, two = some2[propertyName] as? NSDate {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? String, two = some2[propertyName] as? String {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? NSNumber, two = some2[propertyName] as? NSNumber {
return one.compare(two) == sortMode
} else {
fatalError("filteredFirstHalf shouldn't be here")
}
}
//this will always put the blanks behind the sorted
dataModel = sortedAndFiltered + filteredSecondHalf
谢谢!
这是我会做的。如果可以的话,我会通过给它一个 Name 和 Age 而不仅仅是键和值来使结构更具体。不过,这应该为您提供了实现该目标的大纲!
struct PersonInfo {
var key: String!
var value: AnyObject?
init(key key: String, value: AnyObject?) {
self.key = key
self.value = value
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
for key in possibleKeys {
if let value = infos[key] {
personInfos.append(PersonInfo(key: key, value: value))
}
}
}
personInfos.sortInPlace({[=10=].value as? Int > .value as? Int})
}
}
为了方便起见,请点击此处:
struct PersonInfo {
var key: String!
var objectId: Int!
var name: String?
var age: Int?
init(key key: String, objectId: Int, name: String?, age: Int?) {
self.key = key
self.objectId = objectId
self.name = name
self.age = age
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
var objectId: String!
var name: String? = nil
var age: Int? = nil
for key in possibleKeys {
if let value = infos[key] {
if key == "ObjectId" {
objectId = value as? String
}
if key == "Name" {
name = value as? String
}
if key == "Age" {
age = value as? Int
}
}
}
personInfos.append(PersonInfo(key: key, objectId: objectId, name: String?, age: Int?))
}
//by objectId
personInfos.sortInPlace({[=11=].objectId? > .objectId?})
//by age
personInfos.sortInPlace({[=11=].age? > .age?})
//byName
personInfos.sortInPlace({[=11=].name?.compare(.name?) == NSComparisonResult.OrderedAscending})
}
}
要求
所以你有一个字典数组。
let dictionaries: [[String:AnyObject?]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
您要对数组进行排序:
Name
值递增
- 没有
Name
String
的词典应该在末尾
解决方案
这是代码(函数式编程风格)
let sorted = dictionaries.sort { left, right -> Bool in
guard let rightKey = right["Name"] as? String else { return true }
guard let leftKey = left["Name"] as? String else { return false }
return leftKey < rightKey
}
输出
print(sorted)
[
["Name": Optional(Bill)],
["Name": Optional(Frank), "Age": Optional(32)],
["Name": Optional(Susan), "Age": Optional(47)],
["Age": Optional(18)]
]
Swift 无法比较任何两个对象。您必须先将它们转换为特定类型:
let arr: [[String: Any]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
let key = "Name" // The key you want to sort by
let result = arr.sort {
switch ([=10=][key], [key]) {
case (nil, nil), (_, nil):
return true
case (nil, _):
return false
case let (lhs as String, rhs as String):
return lhs < rhs
case let (lhs as Int, rhs as Int):
return lhs < rhs
// Add more for Double, Date, etc.
default:
return true
}
}
print(result)
如果有多个字典没有指定key
的值,它们将被放在result
数组的末尾,但它们的相对顺序不确定。
创建一个数据类型来表示您的数据:
struct Person
{
let identifier: String
let name: String?
let age: Int?
}
制作提取例程:
func unpack(objects: [[String : Any]]) -> [Person]
{
return objects.flatMap { object in
guard let identifier = object["ObjectID"] as? String else {
// Invalid object
return nil
}
let name = object["Name"] as? String
let age = object["Age"] as? Int
return Person(identifier: identifier, name: name, age: age)
}
}
您的数据类型可以按其字段排序,因为它们具有真实类型。
let objects: [[String : Any]] =
[["ObjectID" : "1", "Name" : "Frank", "Age" : 32],
["ObjectID" : "2", "Name" : "Bill"],
["ObjectID" : "3", "Age" : 18],
["ObjectID" : "4", "Name" : "Susan", "Age" : 47]]
let persons = unpack(objects)
let byName = persons.sort { [=12=].name < .name }
nil
s 与 "before" 任何其他值进行比较;如果你想改变它,你可以编写自己的比较器。
我直接从 Parse 中提取字典数组并将它们显示在 table 中。所以我真的很想使用我收到的数据结构(下面结构奇怪的字典)。
A PFObject
是 [String : AnyObject?]
并且我希望能够按任意键进行排序,因此我不知道对象类型并且某些词典中可能缺少该键。因为在 Parse 中,如果你不给 属性 一个值,它就根本不存在。例如:
[
{
"ObjectId" : "1",
"Name" : "Frank",
"Age" : 32
},
{
"ObjectId" : "2",
"Name" : "Bill"
},
{
"ObjectId" : "3",
"Age" : 18
}
{
"ObjectId" : "4",
"Name" : "Susan",
"Age" : 47
}
]
我希望缺少键的词典总是排在排序后的词典之后。一个例子:
原文Table:
ObjectId Name Age
1 Frank 32
2 Bill
3 18
4 Susan 47
按姓名排序:
ObjectId Name Age
2 Bill
1 Frank 32
4 Susan 47
3 18
由于我对数据模型没有太多控制权,而且它在整个应用程序中的使用受到限制,我更愿意专注于算法解决方案而不是结构化解决方案。
我想出了一个方法来做到这一点,但它似乎效率低下且缓慢,我确信有人可以做得更好。
//dataModel is an array of dictionary objects used as my table source
//sort mode is NSComparisonResult ascending or descending
//propertyName is the dictionary key
//first filter out any objects that dont have this key
let filteredFirstHalf = dataModel.filter({ [=14=][propertyName] != nil })
let filteredSecondHalf = dataModel.filter({ [=14=][propertyName] == nil })
//sort the dictionaries that have the key
let sortedAndFiltered = filteredFirstHalf { some1, some2 in
if let one = some1[propertyName] as? NSDate, two = some2[propertyName] as? NSDate {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? String, two = some2[propertyName] as? String {
return one.compare(two) == sortMode
} else if let one = some1[propertyName] as? NSNumber, two = some2[propertyName] as? NSNumber {
return one.compare(two) == sortMode
} else {
fatalError("filteredFirstHalf shouldn't be here")
}
}
//this will always put the blanks behind the sorted
dataModel = sortedAndFiltered + filteredSecondHalf
谢谢!
这是我会做的。如果可以的话,我会通过给它一个 Name 和 Age 而不仅仅是键和值来使结构更具体。不过,这应该为您提供了实现该目标的大纲!
struct PersonInfo {
var key: String!
var value: AnyObject?
init(key key: String, value: AnyObject?) {
self.key = key
self.value = value
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
for key in possibleKeys {
if let value = infos[key] {
personInfos.append(PersonInfo(key: key, value: value))
}
}
}
personInfos.sortInPlace({[=10=].value as? Int > .value as? Int})
}
}
为了方便起见,请点击此处:
struct PersonInfo {
var key: String!
var objectId: Int!
var name: String?
var age: Int?
init(key key: String, objectId: Int, name: String?, age: Int?) {
self.key = key
self.objectId = objectId
self.name = name
self.age = age
}
}
class ViewController: UIViewController {
var possibleKeys: [String] = ["Name", "Age", "ObjectId"]
var personInfos: [PersonInfo] = []
override func viewDidLoad() {
super.viewDidLoad()
for infos in json {
var objectId: String!
var name: String? = nil
var age: Int? = nil
for key in possibleKeys {
if let value = infos[key] {
if key == "ObjectId" {
objectId = value as? String
}
if key == "Name" {
name = value as? String
}
if key == "Age" {
age = value as? Int
}
}
}
personInfos.append(PersonInfo(key: key, objectId: objectId, name: String?, age: Int?))
}
//by objectId
personInfos.sortInPlace({[=11=].objectId? > .objectId?})
//by age
personInfos.sortInPlace({[=11=].age? > .age?})
//byName
personInfos.sortInPlace({[=11=].name?.compare(.name?) == NSComparisonResult.OrderedAscending})
}
}
要求
所以你有一个字典数组。
let dictionaries: [[String:AnyObject?]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
您要对数组进行排序:
Name
值递增- 没有
Name
String
的词典应该在末尾
解决方案
这是代码(函数式编程风格)
let sorted = dictionaries.sort { left, right -> Bool in
guard let rightKey = right["Name"] as? String else { return true }
guard let leftKey = left["Name"] as? String else { return false }
return leftKey < rightKey
}
输出
print(sorted)
[
["Name": Optional(Bill)],
["Name": Optional(Frank), "Age": Optional(32)],
["Name": Optional(Susan), "Age": Optional(47)],
["Age": Optional(18)]
]
Swift 无法比较任何两个对象。您必须先将它们转换为特定类型:
let arr: [[String: Any]] = [
["Name" : "Frank", "Age" : 32],
["Name" : "Bill"],
["Age" : 18],
["Name" : "Susan", "Age" : 47]
]
let key = "Name" // The key you want to sort by
let result = arr.sort {
switch ([=10=][key], [key]) {
case (nil, nil), (_, nil):
return true
case (nil, _):
return false
case let (lhs as String, rhs as String):
return lhs < rhs
case let (lhs as Int, rhs as Int):
return lhs < rhs
// Add more for Double, Date, etc.
default:
return true
}
}
print(result)
如果有多个字典没有指定key
的值,它们将被放在result
数组的末尾,但它们的相对顺序不确定。
创建一个数据类型来表示您的数据:
struct Person
{
let identifier: String
let name: String?
let age: Int?
}
制作提取例程:
func unpack(objects: [[String : Any]]) -> [Person]
{
return objects.flatMap { object in
guard let identifier = object["ObjectID"] as? String else {
// Invalid object
return nil
}
let name = object["Name"] as? String
let age = object["Age"] as? Int
return Person(identifier: identifier, name: name, age: age)
}
}
您的数据类型可以按其字段排序,因为它们具有真实类型。
let objects: [[String : Any]] =
[["ObjectID" : "1", "Name" : "Frank", "Age" : 32],
["ObjectID" : "2", "Name" : "Bill"],
["ObjectID" : "3", "Age" : 18],
["ObjectID" : "4", "Name" : "Susan", "Age" : 47]]
let persons = unpack(objects)
let byName = persons.sort { [=12=].name < .name }
nil
s 与 "before" 任何其他值进行比较;如果你想改变它,你可以编写自己的比较器。