【问题标题】:efficient sorting of an array based on another array's order基于另一个数组的顺序对数组进行有效排序
【发布时间】:2017-08-11 04:21:08
【问题描述】:

假设我有这个:

struct Pet {
    let name: String
}

let pets = [Pet(name: "Z"), Pet(name: "F"), Pet(name: "A"), Pet(name: "E")]
let petNames = ["E", "F", "Z", "A"]

我的预期输出是:

[Pet(name: "E"), Pet(name: "F"), Pet(name: "Z"), Pet(name: "A")]

如何按照petNames'的顺序高效地对pets进行排序?

我目前的方法似乎非常低效:

var sortedPets = [Pet]()
for n in petNames {
    sortedPets.append(pets.first(where: { return $0.name == n })!)
}

我可以使用任何函数式方法吗?

【问题讨论】:

  • This thread 与您的相似。
  • 基于另一个数组创建新数组确实似乎比尝试排序要快得多
  • @Tj3n 在一般情况下不是这样,但是对于这么短的输入数组和排序顺序数组来说这是可能的

标签: ios arrays swift


【解决方案1】:

效率不高,但从功能上解决了问题:

let pets2 = pets.sorted{petNames.index(of:$0.name)! < petNames.index(of:$1.name)!}

现在我们知道我们要做什么了,这更精细但效率更高,因为字典查找速度很快:

var d = [String:Int]()
zip(petNames, 0..<petNames.count).forEach { d[$0.0] = $0.1 }
let pets2 = pets.sorted{d[$0.name]! < d[$1.name]!}

【讨论】:

    【解决方案2】:

    我为此编写了一个扩展。它非常灵活,因为它允许您定义输入按哪个字段排序,以及如何处理未根据 sortOrder 列表定义排序顺序的元素。

    它很长,所以我建议你把它藏在自己的文件里:

    enum UnspecifiedItemSortingPolicy {
        case first
        case last
        case omitEntirely
        case assertAllItemsHaveDefinedSorting
    }
    
    extension MutableCollection {
        typealias Element = Iterator.Element
    
        func sorted<T: Equatable>(
                byOrderOf sortingMemberDeriver: @escaping (Element) -> T,
                in sortingOrder: [T],
                sortUnspecifiedItems unspecifiedItemSortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
            ) -> [Element] {
    
            switch unspecifiedItemSortingPolicy {
                case .omitEntirely: return self
                        .lazy
                        .map { (element: Element) -> (element: Element, position: Int) in
                            let sortingMember = sortingMemberDeriver(element)
                            guard let position = sortingOrder.index(of: sortingMember) else {
                                fatalError("Attempted to sort a collection (\(self)) containing an item (\(element)) whose ordering was not defined in the sorting order: \(sortingOrder).")
                            }
                            return (element: element, position: position)
                        }
                        .sorted{ $0.position < $1.position }
                        .map{ $0.element }
                case .assertAllItemsHaveDefinedSorting: return self
                        .lazy
                        .flatMap { (element: Element) -> (element: Element, position: Int)? in
                            let sortingMember = sortingMemberDeriver(element)
                            return sortingOrder.index(of: sortingMember).map{ (element: element, position: $0) }
                        }
                        .sorted{ $0.position < $1.position }
                        .map{ $0.element }
    
                case .first, .last:
                    var unspecifiedItems = Array<Element>() //items whose ordering isn't in the sortingOrder
    
                    let sortedPortion = self.flatMap { (element: Element) -> (element: Element, position: Int)? in
                            let sortingMember = sortingMemberDeriver(element)
                            guard let position = sortingOrder.index(of: sortingMember) else {
                                unspecifiedItems.append(element)
                                return nil
                            }
                            return (element: element, position: position)
                        }
                        .sorted{ $0.position < $1.position }
                        .map{ $0.element }
    
                    switch unspecifiedItemSortingPolicy {
                        case .first: return unspecifiedItems + sortedPortion
                        case .last: return sortedPortion + unspecifiedItems
                        default: fatalError("This should never happen.")
                    }
            }
        }
    }
    
    extension MutableCollection where Iterator.Element: Equatable {
        func sorted(
                byOrderIn sortingOrder: [Element],
                sortUnspecifiedItems unspecifiedItemSortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
            ) -> [Element] {
            return self.sorted(byOrderOf: {$0}, in: sortingOrder, sortUnspecifiedItems: unspecifiedItemSortingPolicy)
        }
    }
    

    从那里开始,使用起来真的很简单和优雅:

    struct Pet {
        let name: String
    }
    
    let pets = [Pet(name: "Z"), Pet(name: "F"), Pet(name: "A"), Pet(name: "E")]
    let petNames = ["E", "F", "Z", "A"]
    
    let sorted = pets.sorted(byOrderOf: { $0.name }, in: petNames, sortUnspecifiedItems: .last)
    sorted.forEach{ print($0) }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-12
      • 1970-01-01
      • 1970-01-01
      • 2013-11-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多