title: Swift sorting on arbitrary types
body: <p>I have a Set of instances of type <code>Thingie</code>, and I want to provide arrays of Thingies sorted on any property of Thingie. Some of the properties are Int, for instance, while others are String, and there could be others. So I wanted to create a sort routine that accepts a string as the name of the property and compares the two properties of two thingies to determine the order.</p>
  
  <p>It seemed like a job for generics, and I'm getting close, but there's a hole.</p>
  
  <p>Here's where I'm at right now:</p>
  
  <pre><code>func compare&lt;T:Comparable&gt;(lft: T, _ rgt: T) -&gt; Bool {
      return lft &lt; rgt
  }
  
  func orderBy(sortField: String) -&gt; [Thingie] {
      let allArray = (self.thingies as NSSet).allObjects as! [Thingie]
  
      //typealias T = the type of allArray[0][sortField]
      // or maybe create an alias that conforms to a protocol:
      //typealias T:Comparable = ?
  
      return allArray.sort({(a, b) -&gt; Bool in
          return self.compare(a[sortField] as! T, b[sortField] as! T)
      })
  }
  </code></pre>
  
  <p>I created a compare function using generics, and invoke it in my sort routine. The catch is that <code>AnyObject!</code> will not work for my generic, so I need to cast the values returned from <code>a[sortField]</code> and <code>b[sortField]</code> to be of the same type. It doesn't even really matter what type as long as the compiler is happy that both values are of the same type and that it implements the <code>Comparable</code> protocol.</p>
  
  <p>I figured a typealias would do the trick, but maybe there's a better way?</p>
  
  <p>Side question: surely there's a better way to create the initial, unsorted array from the set without resorting to NSSet. A little hint would be welcome. [Solved that bit! Thanks, Oliver Atkinson!]</p>
  
  <p>Here's a big 'ol chunk of code you can paste into a playground. It has three attempts at the orderBy implementation, each with a problem.</p>
  
  <pre><code>//: Playground - noun: a place where people can play
  
  import Foundation
  
  class Thingie: Hashable {
      var data: [String: AnyObject]
      var hashValue: Int
  
      init(data: [String: AnyObject]) {
          self.data = data
          self.hashValue = (data["id"])!.hashValue
      }
  
      subscript(propName: String) -&gt; AnyObject! {
          return self.data[propName]
      }
  
  }
  
  func ==(lhs: Thingie, rhs: Thingie) -&gt; Bool {
      return lhs.hashValue == rhs.hashValue
  }
  
  
  var thingies: Set = Set&lt;Thingie&gt;()
  thingies.insert(Thingie(data: ["id": 2, "description": "two"]));
  thingies.insert(Thingie(data: ["id": 11, "description": "eleven"]));
  
  
  // attempt 1
  // won't compile because '&lt;' won't work when type is ambiguous e.g., AnyObject
  func orderByField1(sortField: String) -&gt; [Thingie] {
      return thingies.sort { $0[sortField] &lt; $1[sortField] }
  }
  
  
  
  
  // compare function that promises the compiler that the operands for &lt; will be of the same type:
  func compare&lt;T:Comparable&gt;(lft: T, _ rgt: T) -&gt; Bool {
      return lft &lt; rgt
  }
  
  
  // attempt 2
  // This compiles but will bomb at runtime if Thingie[sortField] is not a string
  func orderByField2(sortField: String) -&gt; [Thingie] {
      return thingies.sort { compare($0[sortField] as! String, $1[sortField] as! String) }
  }
  
  
  // attempt 3
  // Something like this would be ideal, but protocol Comparable can't be used like this.
  // I suspect the underlying reason that Comparable can't be used as a type is the same thing preventing me from making this work.
  func orderByField3(sortField: String) -&gt; [Thingie] {
      return thingies.sort { compare($0[sortField] as! Comparable, $1[sortField] as! Comparable) }
  }
  
  
  
  // tests - can't run until a compiling candidate is written, of course
  
  // should return array with thingie id=2 first:
  var thingieList: Array = orderByField2("id");
  print(thingieList[0]["id"])
  
  // should return array with thingie id=11 first:
  var thingieList2: Array = orderByField2("description");
  print(thingieList2[0]["id"])
  </code></pre>