我想基于定义元素顺序的自定义数组对数组的元素进行排序。例如,假设以下数组:
let arr = ["second", "first", "second", "fourth", "third", "second"]
我尝试创建一个Array扩展,以便能够通过以下方式对此数组进行排序:
let sortedArr = arr.sortBy(orderArray: ["first","second","third","fourth"])
// desired output: ["first", "second", "second", "second", "third", "fourth":
但是,扩展程序无法正常运行:
extension Array {
public func sortBy<T: Comparable>(orderArray: [T]) -> [T]? {
guard self.count > 0,
self.first! as? T != nil else {
return nil
}
let indices = self.map {orderArray.index(of: $0 as! T)! }
return self.sorted { indices[$0] > indices[$1] } // This doesn’t work
}
}
有什么想法吗?
答案 0 :(得分:3)
您的代码存在的一个问题是self.sorted
期望a
闭包比较数组元素,而不是索引。
这是一种可能的解决方案,也避免了不必要的 类型转换和解包(解释内联):
extension Array where Element: Equatable {
public func sortBy(orderArray: [Element]) -> [Element]? {
// Index of each element in `orderArray`:
let targetIndices = self.flatMap { orderArray.index(of: $0) }
// Verify that each array element occurs in `orderArray`:
guard targetIndices.count == self.count else {
return nil
}
// Sort array indices according to their index in `orderArray`:
let sortedIndices = self.indices.sorted { targetIndices[$0] < targetIndices[$1] }
// Rearrange elements accordingly:
return sortedIndices.map { self[$0] }
}
}
示例:
let arr = ["second", "first", "second", "fourth", "third", "second"]
if let sortedArr = arr.sortBy(orderArray: ["first","second","third","fourth"]) {
print(sortedArr)
// ["first", "second", "second", "second", "third", "fourth"]
}
移动orderArray
中未包含的数组元素
在排序结果的末尾(但保留它们的相对顺序),稍微修改代码
extension Array where Element: Equatable {
public func sortBy(orderArray: [Element]) -> [Element] {
// Index of each element in `orderArray`:
let targetIndices = self.enumerated().map {
orderArray.index(of: $1) ?? orderArray.count + $0
}
// Sort array indices according to their index in `orderArray`:
let sortedIndices = self.indices.sorted { targetIndices[$0] < targetIndices[$1] }
// Rearrange elements accordingly:
return sortedIndices.map { self[$0] }
}
}
示例:
let arr = ["x", "second", "first", "y", "second", "fourth", "third", "second", "z"]
let sortedArr = arr.sortBy(orderArray: ["first","second","third","fourth"])
print(sortedArr)
// ["first", "second", "second", "second", "third", "fourth", "x", "y", "z"]
答案 1 :(得分:2)
orderArray
发出以构造一个排序数组另一种选择(&#34;另一种方式&#34;与@MartinR:s answer相比)是从orderArray
发出并简单地构建&#34;排序&#34;数组基于要排序的数组中orderArray
中元素的出现次数。
子替代#1:允许orderArray
不全面w.r.t.要排序的元素
在下面这个替代方案的示例实现中,要排序的数组中orderArray
中没有的元素与原始数组中的元素保持相同的相对顺序,但是在部分的末尾做的数组的排序(即,在orderArray
中包含 的任何元素之后)。如果nil
未完全覆盖要排序的数组中的所有元素,则另一个替代方法可能是orderArray
返回。
extension Array where Element: Equatable {
/* help method */
private func countInstances(of element: Element) -> Int {
return reduce(0) { $0 + ($1 == element ? 1 : 0) }
}
/* sort by ordered array method */
func sortBy(orderArray: [Element]) -> [Element] {
// construct sorted array based on elements in orderArray
return orderArray
.reduce([]) { $0 + Array(repeating: $1, count: countInstances(of: $1)) } +
filter { !orderArray.contains($0) }
}
}
使用示例:
let arr1 = ["second", "first", "unsortable", "second",
"anotherUnsortable", "fourth", "third", "second"]
let arr2 = ["foo", "first", "bar"]
let orderArray = ["first", "second", "third", "fourth"]
print(arr1.sortBy(orderArray: orderArray))
// ["first", "second", "second", "second", "third", "fourth", "unsortable", "anotherUnsortable"]
print(arr2.sortBy(orderArray: orderArray))
// ["first", "foo", "bar"]
次要选择#2:对于非全面的orderArray
,请返回nil
如果nil
不全面,请回复orderArray
w.r.t.要排序的数组成员,您可以相应地修改上面的sortBy
方法:
extension Array where Element: Equatable {
private func countInstances(of element: Element) -> Int {
return reduce(0) { $0 + ($1 == element ? 1 : 0) }
}
func sortBy(orderArray: [Element]) -> [Element]? {
guard reduce(true, { $0 && orderArray.contains($1) }) else { return nil }
return orderArray
.reduce([]) { $0 + Array(repeating: $1, count: countInstances(of: $1)) }
}
}
使用示例:
let arr1 = ["second", "first", "second",
"fourth", "third", "second"]
let arr2 = ["foo", "baz", "bar"]
let orderArray = ["first", "second", "third", "fourth"]
print(arr1.sortBy(orderArray: orderArray))
// Optional(["first", "second", "second", "second", "third", "fourth"])
print(arr2.sortBy(orderArray: orderArray))
// nil