与 Swift 中的另一个数组相比对数组重新排序
Reorder array compared to another array in Swift
我在 Swift 中有一个数组包含一些对象的 ID,我还有另一个数组包含这些对象及其 ID,所以像这样:
class MyObject {
let id: String
...
}
let ids: [String] = ...
let objects: [MyObject] = ...
现在 ID 数组已按某种自定义顺序排序,我需要按相同顺序对对象数组进行排序。例如:
let ids: [String] = ["1", "2", "3"]
let objects: [MyObject] = [obj3, obj1, obj2]
// obj1.id = "1"
// obj2.id = "2"
// obj3.id = "3"
objects.reorder(template: ids)
// Now objects = [obj1, obj2, obj3]
所以我基本上需要实现这个reorder方法。
有没有人对如何在 Swift 中执行此操作有明智的建议?我正在使用 Swift 3,所以我可以使用所有新的 API。
您可以对 objects
进行排序,以便遵循 ids
写作中的顺序
let sorted = objects.sort { ids.indexOf([=10=].id) < ids.indexOf(.id) }
// [{id "1"}, {id "2"}, {id "3"}]
另一个例子
let ids: [String] = ["3", "2", "1"]
let objects: [MyObject] = [MyObject(id: "3"), MyObject(id: "1"), MyObject(id: "2")]
let sorted = objects.sort { ids.indexOf([=11=].id) < ids.indexOf(.id) }
// [{id "3"}, {id "2"}, {id "1"}]
This code is in Swift 2.2
Swift 4.2 解决方案
let sorted = objects.sorted { ids.index(of: [=12=].id)! < ids.index(of: .id)! }
可能的解决方案:
let sorted = objects.flatMap { obj in
ids.index(of: obj.id).map { idx in (obj, idx) }
}.sorted(by: { [=10=].1 < .1 } ).map { [=10=].0 }
解释:
- 首先,每个对象都与相应的对象绑定在一起
id 在数组中的位置,这给出了一个数组
(object, index)
个元组。
- 此数组已根据索引位置排序。
- 最后,再次提取对象。
可能的优势:
- 每个对象只在数组中搜索一次。
- 忽略数组中不存在 id 的对象。
请注意,如果在 unsorted
中找不到 ID,重新排序方法将失败
func reorder(items: [MyObject], order : [String]) -> [String] {
var unsorted = items
var ordered : [String] = []
for orderId in order {
let index = unsorted.filter{ [=10=].id == orderId }.first
let found = unsorted.remove(at: index!)
ordered.append(found)
}
return ordered
}
我不是计算机科学专业的人,但在我看来,这种反复调用 indexOf
的做法很浪费。当然,正确的方法是提前一次遍历模板数组(ids
),构建一个字典,将每个元素与其在数组中的位置相关联:
let ids = ["1", "2", "3"]
var d = [String:Int]()
for (ix,id) in ids.enumerated() {
d[id] = ix
}
现在我们有了字典,在字典中查找很快。所以我们可以使用每个对象的 id
作为键,并根据相应的值进行排序。假设这是我们的初始对象数组:
class MyObject {
let id: String
init(id:String) {self.id = id}
}
let objects = [MyObject(id:"3"), MyObject(id:"1"), MyObject(id:"2")]
现在排序是 one-liner:
let objectsSorted = objects.sorted { d[[=12=].id]! < d[.id]! }
如果你知道你将经常使用 ids
作为模板,这里的优势尤其明显,因为你只需要形成字典 d
一次 现在您可以根据该模板多次排序了。实际上,字典记住了排序顺序。 (当然,我没有考虑如果字典查找失败会发生什么;我们就崩溃了。)
我们可以推广这种方法,基于这样一个事实,即为了用作字典键,模板数组中的值必须是可散列的:
struct Cosorter<K:Hashable> {
let d : [K:Int]
init(_ arr:[K]) {
var dd = [K:Int]()
for (ix,val) in arr.enumerated() {
dd[val] = ix
}
self.d = dd
}
func lt(_ v1:K, _ v2:K) -> Bool {
return self.d[v1]! < self.d[v2]!
}
}
现在每个模板都被转换成一个用模板数组初始化的Cosorter实例,从而导致准备好字典,一次:
let idsTemplate = Cosorter(ids)
任何时候我们想要在该模板上进行排序,我们只需使用该模板的 lt
作为排序函数:
let objectsSorted = objects.sorted {idsTemplate.lt([=15=].id,.id)}
我在 Swift 中有一个数组包含一些对象的 ID,我还有另一个数组包含这些对象及其 ID,所以像这样:
class MyObject {
let id: String
...
}
let ids: [String] = ...
let objects: [MyObject] = ...
现在 ID 数组已按某种自定义顺序排序,我需要按相同顺序对对象数组进行排序。例如:
let ids: [String] = ["1", "2", "3"]
let objects: [MyObject] = [obj3, obj1, obj2]
// obj1.id = "1"
// obj2.id = "2"
// obj3.id = "3"
objects.reorder(template: ids)
// Now objects = [obj1, obj2, obj3]
所以我基本上需要实现这个reorder方法。 有没有人对如何在 Swift 中执行此操作有明智的建议?我正在使用 Swift 3,所以我可以使用所有新的 API。
您可以对 objects
进行排序,以便遵循 ids
写作中的顺序
let sorted = objects.sort { ids.indexOf([=10=].id) < ids.indexOf(.id) }
// [{id "1"}, {id "2"}, {id "3"}]
另一个例子
let ids: [String] = ["3", "2", "1"]
let objects: [MyObject] = [MyObject(id: "3"), MyObject(id: "1"), MyObject(id: "2")]
let sorted = objects.sort { ids.indexOf([=11=].id) < ids.indexOf(.id) }
// [{id "3"}, {id "2"}, {id "1"}]
This code is in Swift 2.2
Swift 4.2 解决方案
let sorted = objects.sorted { ids.index(of: [=12=].id)! < ids.index(of: .id)! }
可能的解决方案:
let sorted = objects.flatMap { obj in
ids.index(of: obj.id).map { idx in (obj, idx) }
}.sorted(by: { [=10=].1 < .1 } ).map { [=10=].0 }
解释:
- 首先,每个对象都与相应的对象绑定在一起
id 在数组中的位置,这给出了一个数组
(object, index)
个元组。 - 此数组已根据索引位置排序。
- 最后,再次提取对象。
可能的优势:
- 每个对象只在数组中搜索一次。
- 忽略数组中不存在 id 的对象。
请注意,如果在 unsorted
func reorder(items: [MyObject], order : [String]) -> [String] {
var unsorted = items
var ordered : [String] = []
for orderId in order {
let index = unsorted.filter{ [=10=].id == orderId }.first
let found = unsorted.remove(at: index!)
ordered.append(found)
}
return ordered
}
我不是计算机科学专业的人,但在我看来,这种反复调用 indexOf
的做法很浪费。当然,正确的方法是提前一次遍历模板数组(ids
),构建一个字典,将每个元素与其在数组中的位置相关联:
let ids = ["1", "2", "3"]
var d = [String:Int]()
for (ix,id) in ids.enumerated() {
d[id] = ix
}
现在我们有了字典,在字典中查找很快。所以我们可以使用每个对象的 id
作为键,并根据相应的值进行排序。假设这是我们的初始对象数组:
class MyObject {
let id: String
init(id:String) {self.id = id}
}
let objects = [MyObject(id:"3"), MyObject(id:"1"), MyObject(id:"2")]
现在排序是 one-liner:
let objectsSorted = objects.sorted { d[[=12=].id]! < d[.id]! }
如果你知道你将经常使用 ids
作为模板,这里的优势尤其明显,因为你只需要形成字典 d
一次 现在您可以根据该模板多次排序了。实际上,字典记住了排序顺序。 (当然,我没有考虑如果字典查找失败会发生什么;我们就崩溃了。)
我们可以推广这种方法,基于这样一个事实,即为了用作字典键,模板数组中的值必须是可散列的:
struct Cosorter<K:Hashable> {
let d : [K:Int]
init(_ arr:[K]) {
var dd = [K:Int]()
for (ix,val) in arr.enumerated() {
dd[val] = ix
}
self.d = dd
}
func lt(_ v1:K, _ v2:K) -> Bool {
return self.d[v1]! < self.d[v2]!
}
}
现在每个模板都被转换成一个用模板数组初始化的Cosorter实例,从而导致准备好字典,一次:
let idsTemplate = Cosorter(ids)
任何时候我们想要在该模板上进行排序,我们只需使用该模板的 lt
作为排序函数:
let objectsSorted = objects.sorted {idsTemplate.lt([=15=].id,.id)}