0

thanks for reading my post.

I have an array of tuples declared as such:

var myArray: [(item1: String?, item2: NSDate?)] = []

At the end of my loop I want to sort my array of tuples based on every tuple's item2, whose type is NSDate?.

Based on this answer and this answer I tried the following, but received this compiler error, "cannot invoke 'sort' with an argument list of type '((_,_) -> _)'.

Here is what I tried:

myArray.sort {$0.1.item2?.compare($1.1.item2?) == NSComparisonResult.OrderedDescending }

P.S. println() works fine and prints item1 and item2 as an optional.

Community
  • 1
  • 1
Sami
  • 579
  • 5
  • 25
  • 4
    it is not good practice to use tuples in data structures, use an array or dictionary instead. From the Apple Swift iBook: “Tuples are useful for temporary groups of related values. They are not suited to the creation of complex data structures. **If your data structure is likely to persist beyond a temporary scope, model it as a class or structure, rather than as a tuple.**” Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2 Prerelease).” iBooks. https://itun.es/us/k5SW7.l – zaph Sep 15 '15 at 20:06

3 Answers3

1

You must implement Comparable protocol to NSDate

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

extension NSDate: Comparable { }

After that you can sort your tuples by date:

myArray!.sort {$0.1 == $1.1 ? $0.1 > $1.1 : $0.1 > $1.1 }
Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
Kristijan Delivuk
  • 1,253
  • 13
  • 24
  • Thanks for your response. I just tried your solution, it did not work. [according here](http://stackoverflow.com/questions/26577496/how-do-i-sort-a-swift-array-containing-instances-of-nsmanagedobject-subclass-by) I cannot compare `NSDate` directly with `<`. Could it be something else? – Sami Sep 15 '15 at 20:04
  • Yep, this worked thanks! However @zaph mentioned I should consider switching to arrays or dictionaries so I'll probably end up doing that so I stick to good practice. Thanks for the quick response. – Sami Sep 15 '15 at 20:19
  • 1
    Swift dictionary isn't sortable by default so its better to implement array of this structure if you need sorting. – Kristijan Delivuk Sep 15 '15 at 20:28
0

An alternative to the accepted solution:

let res = myArray.sort { (left, right) -> Bool in
    return left.item2?.timeIntervalSinceReferenceDate < right.item2?.timeIntervalSinceReferenceDate
}
MirekE
  • 11,515
  • 5
  • 35
  • 28
0

The reason why the linked solutions did not work is because the array of tuples defined in the question contains optional types.

Checking for the optionals fixes the problem, without having to add new operators to NSDate.

An example, with 3 dates, and optional types:

var myArray: [(item1: String?, item2: NSDate?)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]

myArray.sortInPlace { (lhs, rhs) -> Bool in
    if lhs.item2 != nil && rhs.item2 != nil {
        return lhs.item2!.compare(rhs.item2!) == .OrderedAscending
    }
    return false // Return true if you want nil values first
}

Same code, if the types didn't allow for optionals:

var myArray: [(item1: String, item2: NSDate)] = []
myArray = [("now", NSDate()), ("now+30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(30))), ("now-30s", NSDate().dateByAddingTimeInterval(NSTimeInterval(-30)))]

myArray.sortInPlace { (lhs, rhs) -> Bool in
    return lhs.item2.compare(rhs.item2) == .OrderedAscending
}

MirekE's solution also works well, but you do not have control on where the nil values would end (they will be at the beginning).

Eneko Alonso
  • 18,884
  • 9
  • 62
  • 84