【问题标题】:How do I sort an array of structs by multiple values?如何按多个值对结构数组进行排序?
【发布时间】:2015-01-18 09:00:11
【问题描述】:

我已经有了按 1 个值排序的代码,如下所示,但我想知道如何使用多个值进行排序?我想按 set 排序,然后按 someString 排序。

一个是整数,在这种情况下一个是字符串。我曾考虑将整数转换为字符串,然后将它们连接起来,但认为必须有更好的方法,因为将来我可能有 2 个整数要排序。

struct Condition {
    var set = 0
    var someString = ""
}

var conditions = [Condition]()

conditions.append(Condition(set: 1, someString: "string3"))
conditions.append(Condition(set: 2, someString: "string2"))
conditions.append(Condition(set: 3, someString: "string7"))
conditions.append(Condition(set: 1, someString: "string9"))
conditions.append(Condition(set: 2, someString: "string4"))
conditions.append(Condition(set: 3, someString: "string0"))
conditions.append(Condition(set: 1, someString: "string1"))
conditions.append(Condition(set: 2, someString: "string6"))

// sort
let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
    return (lhs.set) < (rhs.set)
}

// printed sorted conditions
for index in 0...conditions.count-1 {
    println("\(sorted[index].set) - \(sorted[index].someString)")
}

【问题讨论】:

    标签: sorting swift


    【解决方案1】:

    我还不精通 Swift,但多标准排序的基本思想是:

    let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
        if lhs.set == rhs.set {
            return lhs.someString < rhs.someString
        }
        return (lhs.set) < (rhs.set)
    }
    

    【讨论】:

      【解决方案2】:

      即使前面的答案在请求的情况下完全没问题,我想为此提出一个更通用的方法:

      
      infix operator &lt=> {
      associativity none
      precedence 130
      }
      func &lt=> &ltT: Comparable>(lhs: T, rhs: T) -> NSComparisonResult {
          return lhs &lt rhs ? .OrderedAscending : lhs == rhs ? .OrderedSame : .OrderedDescending
      }
      private func _sortedLexicographically&ltS: SequenceType&gt(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] {
          return sorted(source, { lhs, rhs in
              for compare in comparators {
                  switch compare(lhs, rhs) {
                  case .OrderedAscending: return true
                  case .OrderedDescending: return false
                  case .OrderedSame: break
                  }
              }
              return false
          })
      }
      public func sortedLexicographically&ltS: SequenceType>(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] {
          return _sortedLexicographically(source, comparators)
      }
      extension Array {
          func sortedLexicographically(comparators: [(Element, Element) -> NSComparisonResult]) -> [Element] {
              return _sortedLexicographically(self, comparators)
          }
      }
      

      从这里可以很容易地按照要求进行订购:

      
      struct Foo {
          var foo: Int
          var bar: Int
          var baz: Int
      }
      let foos = [Foo(foo: 1, bar: 2, baz: 3), Foo(foo: 1, bar: 3, baz: 1), Foo(foo: 0, bar: 4, baz: 2), Foo(foo: 2, bar: 0, baz: 0), Foo(foo: 1, bar: 2, baz: 2)]
      let orderedFoos = foos.sortedLexicographically([{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }])
      

      如果该类型的这种比较是该类型本身固有的,而不是您需要的仅一个位置的排序,您可以采用更类似于 stdlib 的方法并改为扩展 Comparable

      
      extension Foo: Comparable {}
      func == (lhs: Foo, rhs: Foo) -> Bool {
          return lhs.foo == rhs.foo && lhs.bar == rhs.bar && lhs.baz == rhs.baz
      }
      func &lt (lhs: Foo, rhs: Foo) -> Bool {
          let comparators: [(Foo, Foo) -> NSComparisonResult] = [{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }]
          for compare in comparators {
              switch compare(lhs, rhs) {
              case .OrderedAscending: return true
              case .OrderedDescending: return false
              case .OrderedSame: break
              }
          }
          return false
      }
      let comparableOrderedFoos = sorted(foos)
      

      还有另一种可能的方法是创建一个LexicographicallyComparable 协议,该协议说明哪些Comparable 字段以及它们具有哪些优先级,但不幸的是,我想不出不使用可变参数泛型的方法, Swift 2.0 不支持这些,同时保持 Swift 代码典型的类型安全。

      【讨论】:

      • 这是正确的方法,恕我直言。如果您要定义一个结构,并且希望能够比较它,那么定义它的顺序是结构
      • 确实,我同意。但是仍然可能存在您想要显示替代排序顺序并且您希望这样做的情况。正如他们所说:YMMV
      • 如果演示了使用 Swift 自定义运算符以及类型泛型来解决这个问题,应该获得额外的积分!太棒了!
      • 惊人的答案!在 Swift 2 中,sorted 改为 sort,现在在集合上调用,即:source.sort(...
      【解决方案3】:

      如果set 的值相同,您将比较someString,否则,使用您现有的比较:

      let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
          if lhs.set == rhs.set {
              return lhs.someString < rhs.someString
          } else {
              return lhs.set < rhs.set
          }
      }
      

      【讨论】:

      • 就是这样。谢谢!
      猜你喜欢
      • 2011-02-23
      • 1970-01-01
      • 2013-09-14
      • 1970-01-01
      • 2021-07-11
      • 2011-03-17
      相关资源
      最近更新 更多