代码之家  ›  专栏  ›  技术社区  ›  RileyDev

按两个值对数组排序Swift 4[重复]

  •  0
  • RileyDev  · 技术社区  · 6 年前

    Contact 物体:

    var contacts:[Contact] = [Contact]()
    

    联系人类别:

    Class Contact:NSOBject {
        var firstName:String!
        var lastName:String!
    }
    

    lastName 然后 firstName 以防有些联系人也这样 .

    我可以按其中一个标准来排序,但不能同时按这两个标准来排序。

    contacts.sortInPlace({$0.lastName < $1.lastName})
    

    如何添加更多条件来排序此数组?

    0 回复  |  直到 6 年前
        1
  •  93
  •   Alexander    6 年前

    想想“按多个标准排序”意味着什么。这意味着首先用一个标准比较两个对象。然后,如果这些条件相同,则领带将被下一个条件打断,以此类推,直到获得所需的顺序。

    let sortedContacts = contacts.sort {
        if $0.lastName != $1.lastName { // first, compare by last names
            return $0.lastName < $1.lastName
        }
        /*  last names are the same, break ties by foo
        else if $0.foo != $1.foo {
            return $0.foo < $1.foo
        }
        ... repeat for all other fields in the sorting
        */
        else { // All other fields are tied, break ties by last name
            return $0.firstName < $1.firstName
        }
    }
    

    你现在看到的是 Sequence.sorted(by:) method

    如果您的排序将在许多地方使用,最好使您的类型符合 Comparable protocol Sequence.sorted() method ,它将参考您对 Comparable.<(_:_:) operator 以确定元素的比较方式。这样,你可以把任何 Sequence 属于 Contact 无需复制排序代码。

        2
  •  87
  •   Hamish    6 年前

    按多个条件(即按一个比较排序,如果相等,则按另一个比较排序)执行排序的一种非常简单的方法是使用 元组 ,作为 < > 操作员对执行字典比较的重载。

    /// Returns a Boolean value indicating whether the first tuple is ordered
    /// before the second in a lexicographical ordering.
    ///
    /// Given two tuples `(a1, a2, ..., aN)` and `(b1, b2, ..., bN)`, the first
    /// tuple is before the second tuple if and only if
    /// `a1 < b1` or (`a1 == b1` and
    /// `(a2, ..., aN) < (b2, ..., bN)`).
    public func < <A : Comparable, B : Comparable>(lhs: (A, B), rhs: (A, B)) -> Bool
    

    例如:

    struct Contact {
      var firstName: String
      var lastName: String
    }
    
    var contacts = [
      Contact(firstName: "Leonard", lastName: "Charleson"),
      Contact(firstName: "Michael", lastName: "Webb"),
      Contact(firstName: "Charles", lastName: "Alexson"),
      Contact(firstName: "Michael", lastName: "Elexson"),
      Contact(firstName: "Alex", lastName: "Elexson"),
    ]
    
    contacts.sort {
      ($0.lastName, $0.firstName) <
        ($1.lastName, $1.firstName)
    }
    
    print(contacts)
    
    // [
    //   Contact(firstName: "Charles", lastName: "Alexson"),
    //   Contact(firstName: "Leonard", lastName: "Charleson"),
    //   Contact(firstName: "Alex", lastName: "Elexson"),
    //   Contact(firstName: "Michael", lastName: "Elexson"),
    //   Contact(firstName: "Michael", lastName: "Webb")
    // ]
    

    这将比较元素 lastName < 与他们比较。如果他们 相等,然后它将移动到元组中的下一对元素,即比较 firstName 财产。

    < > 2到6个元素的元组的重载。

    contacts.sort {
      ($1.lastName, $0.firstName) <
        ($0.lastName, $1.firstName)
    }
    
    // [
    //   Contact(firstName: "Michael", lastName: "Webb")
    //   Contact(firstName: "Alex", lastName: "Elexson"),
    //   Contact(firstName: "Michael", lastName: "Elexson"),
    //   Contact(firstName: "Leonard", lastName: "Charleson"),
    //   Contact(firstName: "Charles", lastName: "Alexson"),
    // ]
    

    现在可以按 姓氏 名字 提升。


    sort(by:) 接受多个谓词的重载

    受到以下讨论的启发 Sorting Collections with map closures and SortDescriptors ,另一个选项是定义 排序(按:) sorted(by:) 它处理多个谓词,其中每个谓词依次被考虑以决定元素的顺序。

    extension MutableCollection where Self : RandomAccessCollection {
      mutating func sort(
        by firstPredicate: (Element, Element) -> Bool,
        _ secondPredicate: (Element, Element) -> Bool,
        _ otherPredicates: ((Element, Element) -> Bool)...
      ) {
        sort(by:) { lhs, rhs in
          if firstPredicate(lhs, rhs) { return true }
          if firstPredicate(rhs, lhs) { return false }
          if secondPredicate(lhs, rhs) { return true }
          if secondPredicate(rhs, lhs) { return false }
          for predicate in otherPredicates {
            if predicate(lhs, rhs) { return true }
            if predicate(rhs, lhs) { return false }
          }
          return false
        }
      }
    }
    

    extension Sequence {
      mutating func sorted(
        by firstPredicate: (Element, Element) -> Bool,
        _ secondPredicate: (Element, Element) -> Bool,
        _ otherPredicates: ((Element, Element) -> Bool)...
      ) -> [Element] {
        return sorted(by:) { lhs, rhs in
          if firstPredicate(lhs, rhs) { return true }
          if firstPredicate(rhs, lhs) { return false }
          if secondPredicate(lhs, rhs) { return true }
          if secondPredicate(rhs, lhs) { return false }
          for predicate in otherPredicates {
            if predicate(lhs, rhs) { return true }
            if predicate(rhs, lhs) { return false }
          }
          return false
        }
      }
    }
    

    (在 secondPredicate: 排序(按:)

    这样我们就可以说(使用 contacts 以前的数组):

    contacts.sort(by:
      { $0.lastName > $1.lastName },  // first sort by lastName descending
      { $0.firstName < $1.firstName } // ... then firstName ascending
      // ...
    )
    
    print(contacts)
    
    // [
    //   Contact(firstName: "Michael", lastName: "Webb")
    //   Contact(firstName: "Alex", lastName: "Elexson"),
    //   Contact(firstName: "Michael", lastName: "Elexson"),
    //   Contact(firstName: "Leonard", lastName: "Charleson"),
    //   Contact(firstName: "Charles", lastName: "Alexson"),
    // ]
    
    // or with sorted(by:)...
    let sortedContacts = contacts.sorted(by:
      { $0.lastName > $1.lastName },  // first sort by lastName descending
      { $0.firstName < $1.firstName } // ... then firstName ascending
      // ...
    )
    


    符合 Comparable

    如果你要定期做这些比较 @AMomchilov @appzYourLife 建议,你可以遵从 Contact 可比

    extension Contact : Comparable {
      static func == (lhs: Contact, rhs: Contact) -> Bool {
        return (lhs.firstName, lhs.lastName) ==
                 (rhs.firstName, rhs.lastName)
      }
    
      static func < (lhs: Contact, rhs: Contact) -> Bool {
        return (lhs.lastName, lhs.firstName) <
                 (rhs.lastName, rhs.firstName)
      }
    }
    

    sort() 对于升序:

    contacts.sort()
    

    sort(by: >)

    contacts.sort(by: >)
    

    在嵌套类型中定义自定义排序顺序

    如果要使用其他排序顺序,可以在嵌套类型中定义它们:

    extension Contact {
      enum Comparison {
        static let firstLastAscending: (Contact, Contact) -> Bool = {
          return ($0.firstName, $0.lastName) <
                   ($1.firstName, $1.lastName)
        }
      }
    }
    

    contacts.sort(by: Contact.Comparison.firstLastAscending)
    
        3
  •  13
  •   oyalhi    7 年前

    下面显示了另一种使用两个标准进行排序的简单方法。

    检查第一个字段,在本例中是 lastName 姓氏 姓氏 的值相等,然后按第二个字段排序,在本例中 firstName .

    contacts.sort { $0.lastName == $1.lastName ? $0.firstName < $1.firstName : $0.lastName < $1.lastName  }
    
        4
  •  5
  •   Jaime Allauca    8 年前

    我在Swift 3中创建了一篇关于如何做到这一点的博客文章,并使代码简单易读。

    你可以在这里找到它:

    http://master-method.com/index.php/2016/11/23/sort-a-sequence-i-e-arrays-of-objects-by-multiple-properties-in-swift-3/

    您还可以在这里找到带有以下代码的GitHub存储库:

    https://github.com/jallauca/SortByMultipleFieldsSwift.playground

    所有这些的要点,比如说,如果你有位置列表,你就可以做到:

    struct Location {
        var city: String
        var county: String
        var state: String
    }
    
    var locations: [Location] {
        return [
            Location(city: "Dania Beach", county: "Broward", state: "Florida"),
            Location(city: "Fort Lauderdale", county: "Broward", state: "Florida"),
            Location(city: "Hallandale Beach", county: "Broward", state: "Florida"),
            Location(city: "Delray Beach", county: "Palm Beach", state: "Florida"),
            Location(city: "West Palm Beach", county: "Palm Beach", state: "Florida"),
            Location(city: "Savannah", county: "Chatham", state: "Georgia"),
            Location(city: "Richmond Hill", county: "Bryan", state: "Georgia"),
            Location(city: "St. Marys", county: "Camden", state: "Georgia"),
            Location(city: "Kingsland", county: "Camden", state: "Georgia"),
        ]
    }
    
    let sortedLocations =
        locations
            .sorted(by:
                ComparisonResult.flip <<< Location.stateCompare,
                Location.countyCompare,
                Location.cityCompare
            )
    
        5
  •  5
  •   XueYu    7 年前

    Sort Descriptors in Swift . 我们有几种方法来进行多标准排序。

    1. class Person: NSObject {
          var first: String
          var last: String
          var yearOfBirth: Int
          init(first: String, last: String, yearOfBirth: Int) {
              self.first = first
              self.last = last
              self.yearOfBirth = yearOfBirth
          }
      
          override var description: String {
              get {
                  return "\(self.last) \(self.first) (\(self.yearOfBirth))"
              }
          }
      }
      
      let people = [
          Person(first: "Jo", last: "Smith", yearOfBirth: 1970),
          Person(first: "Joe", last: "Smith", yearOfBirth: 1970),
          Person(first: "Joe", last: "Smyth", yearOfBirth: 1970),
          Person(first: "Joanne", last: "smith", yearOfBirth: 1985),
          Person(first: "Joanne", last: "smith", yearOfBirth: 1970),
          Person(first: "Robert", last: "Jones", yearOfBirth: 1970),
      ]
      

      例如,在这里,我们要按姓氏排序,然后按姓氏排序,最后按出生年份排序。我们希望在不敏感的情况下使用用户区域设置。

      let lastDescriptor = NSSortDescriptor(key: "last", ascending: true,
        selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
      let firstDescriptor = NSSortDescriptor(key: "first", ascending: true, 
        selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
      let yearDescriptor = NSSortDescriptor(key: "yearOfBirth", ascending: true)
      
      
      
      (people as NSArray).sortedArray(using: [lastDescriptor, firstDescriptor, yearDescriptor]) 
      // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]
      
    2. 使用快速的姓/名排序方式。

      let sortedPeople = people.sorted { p0, p1 in
          let left =  [p0.last, p0.first]
          let right = [p1.last, p1.first]
      
          return left.lexicographicallyPrecedes(right) {
              $0.localizedCaseInsensitiveCompare($1) == .orderedAscending
          }
      }
      sortedPeople // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1985), Joanne smith (1970), Joe Smith (1970), Joe Smyth (1970)]
      
    3. 快速进入inmitate NSSortDescriptor。这使用了“函数是一类类型”的概念。SortDescriptor是函数类型,接受两个值,返回bool。假设sortByFirstName,我们取两个参数($0,$1)并比较它们的名字。combine函数使用一组sortdescriptor,比较所有这些函数并发出命令。

      typealias SortDescriptor<Value> = (Value, Value) -> Bool
      
      let sortByFirstName: SortDescriptor<Person> = {
          $0.first.localizedCaseInsensitiveCompare($1.first) == .orderedAscending
      }
      let sortByYear: SortDescriptor<Person> = { $0.yearOfBirth < $1.yearOfBirth }
      let sortByLastName: SortDescriptor<Person> = {
          $0.last.localizedCaseInsensitiveCompare($1.last) == .orderedAscending
      }
      
      func combine<Value>
          (sortDescriptors: [SortDescriptor<Value>]) -> SortDescriptor<Value> {
          return { lhs, rhs in
              for isOrderedBefore in sortDescriptors {
                  if isOrderedBefore(lhs,rhs) { return true }
                  if isOrderedBefore(rhs,lhs) { return false }
              }
              return false
          }
      }
      
      let combined: SortDescriptor<Person> = combine(
          sortDescriptors: [sortByLastName,sortByFirstName,sortByYear]
      )
      people.sorted(by: combined)
      // [Robert Jones (1970), Jo Smith (1970), Joanne smith (1970), Joanne smith (1985), Joe Smith (1970), Joe Smyth (1970)]
      

      这很好,因为您可以将它与struct和class一起使用,甚至可以扩展它以与nils进行比较。

    original article 强烈建议。它有更多的细节和很好的解释。

        6
  •  2
  •   Community ahmed    7 年前

    我建议你用 Hamish's tuple solution


    如果你想要像这样的东西 if statements 但是简化了分支逻辑,您可以使用此解决方案,它允许您执行以下操作:

    animals.sort {
      return comparisons(
        compare($0.family, $1.family, ascending: false),
        compare($0.name, $1.name))
    }
    

    func compare<C: Comparable>(_ value1Closure: @autoclosure @escaping () -> C, _ value2Closure: @autoclosure @escaping () -> C, ascending: Bool = true) -> () -> ComparisonResult {
      return {
        let value1 = value1Closure()
        let value2 = value2Closure()
        if value1 == value2 {
          return .orderedSame
        } else if ascending {
          return value1 < value2 ? .orderedAscending : .orderedDescending
        } else {
          return value1 > value2 ? .orderedAscending : .orderedDescending
        }
      }
    }
    
    func comparisons(_ comparisons: (() -> ComparisonResult)...) -> Bool {
      for comparison in comparisons {
        switch comparison() {
        case .orderedSame:
          continue // go on to the next property
        case .orderedAscending:
          return true
        case .orderedDescending:
          return false
        }
      }
      return false // all of them were equal
    }
    

    如果要测试它,可以使用以下额外代码:

    enum Family: Int, Comparable {
      case bird
      case cat
      case dog
    
      var short: String {
        switch self {
        case .bird: return "B"
        case .cat: return "C"
        case .dog: return "D"
        }
      }
    
      public static func <(lhs: Family, rhs: Family) -> Bool {
        return lhs.rawValue < rhs.rawValue
      }
    }
    
    struct Animal: CustomDebugStringConvertible {
      let name: String
      let family: Family
    
      public var debugDescription: String {
        return "\(name) (\(family.short))"
      }
    }
    
    let animals = [
      Animal(name: "Leopard", family: .cat),
      Animal(name: "Wolf", family: .dog),
      Animal(name: "Tiger", family: .cat),
      Animal(name: "Eagle", family: .bird),
      Animal(name: "Cheetah", family: .cat),
      Animal(name: "Hawk", family: .bird),
      Animal(name: "Puma", family: .cat),
      Animal(name: "Dalmatian", family: .dog),
      Animal(name: "Lion", family: .cat),
    ]
    

    主要区别在于 Jamie's solution $0.family 而不是 Animal.familyCompare . 升序/降序由参数而不是重载运算符控制。Jamie的解决方案在数组上添加了一个扩展,而我的解决方案使用内置的 sort sorted 方法,但需要定义另外两个方法: compare comparisons .

    为了完整起见,下面是我的解决方案与 . 为了证明这一点,我将使用一个疯狂的例子 (name, address, profileViews) 在比较开始之前,Hamish的解将精确地计算6个属性值中的每一个。这可能不需要,也可能不需要。例如,假设 profileViews 除非绝对必要。我的解决方案将避免评估 纵断面图 $0.name == $1.name $0.address == $1.address 纵断面图 它可能会被多次评估。

        7
  •  1
  •   Lou Zell    8 年前

    contacts.sort() { [$0.last, $0.first].lexicographicalCompare([$1.last, $1.first]) }
    
        8
  •  -1
  •   pkamb santoshIOS    6 年前

    在Swift 3中对我的数组[字符串]起作用,在Swift 4中似乎没问题

    array = array.sorted{$0.compare($1, options: .numeric) == .orderedAscending}