Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you sort dates in a dictionary?

Tags:

swift

I am having trouble trying to sort the following dictionary, so that it is printed in oldest date first order when printed.

var dayTotalDicTest: [String:Int] =

            [
                "04-09-2015" : 4,
                "04-10-2015" : 6,
                "04-07-2015" : 8,
                "03-28-2015" : 10,
                "12-10-2014" : 12,
                "12-10-2015" : 12,

            ]
like image 691
JideO Avatar asked Apr 10 '15 02:04

JideO


People also ask

How do you arrange the order of the dictionary?

It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.


3 Answers

edit/update: Xcode 8.2.1 • Swift 3.0.2

extension String {
    static let shortDateUS: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.dateStyle = .short
        return formatter
    }()
    var shortDateUS: Date? {
        return String.shortDateUS.date(from: self)
    }
}

let dayTotalDicTest: [String:Int] = [
    "04-09-2015" : 4,
    "04-10-2015" : 6,
    "04-07-2015" : 8,
    "03-28-2015" : 10,
    "12-10-2014" : 12,
    "12-10-2015" : 12]

let myArrayOfTuples = dayTotalDicTest.sorted{
    guard let d1 = $0.key.shortDateUS, let d2 = $1.key.shortDateUS else { return false }
    return d1 < d2
}

print(myArrayOfTuples)  // [("12-10-2014", 12), ("03-28-2015", 10), ("04-07-2015", 8), ("04-09-2015", 4), ("04-10-2015", 6), ("12-10-2015", 12)]\n"

for tuple in myArrayOfTuples {
    print(tuple)
}
like image 76
Leo Dabus Avatar answered Oct 17 '22 23:10

Leo Dabus


In Swift 3 Date objects are now comparable, we can accomplish what you want as follows:

let dayTotalDicTest: [String:Int] = [
  "04-09-2015" : 4,
  "04-10-2015" : 6,
  "04-07-2015" : 8,
  "03-28-2015" : 10,
  "12-10-2014" : 12,
  "12-10-2015" : 12]

//Create a date formatter to convert our date strings to Date objects
let df = DateFormatter()
df.dateFormat = "MM-dd-yyyy"

let sortedArrayOfDicts = dayTotalDicTest
  //First map to an array tuples: [(Date, [String:Int]]
  .map{(df.date(from: $0.key)!, [$0.key:$0.value])}

  //Now sort by the dates, using `<` since dates are Comparable.
  .sorted{$0.0 < $1.0}

  //And re-map to discard the Date objects
  .map{$1}

for item in sortedArrayOfDicts {
  print(item)
}

The code above does a one-time mapping of the key/value pairs to tuples where the first element is a Date object, and the second entry in the tuple is a single-element dictionary containing the oringinal key/value pair. It then sorts the array of tuples, and then maps the sorted array to extract the key/value pairs, so what you're left with is an array of single-element dictionaries from the original data structure.

When you sort, you want to avoid expensive operations during the comparison operations of the sort. Converting date strings to dates is fairly slow. Doing that conversion during each comparison in the sort is extra inefficient, because you end up comparing each element in your array to multiple other elements, so you do the string-to-date conversions over and over.

like image 28
Duncan C Avatar answered Oct 17 '22 23:10

Duncan C


Dictionaries cannot be sorted, so you have to convert it to an array. Either map or sorted can do that for you. Also, the strings cannot be sorted as provided, so you either need to restructure the string into yyyy-MM-dd format, or convert it to a date.

But once you've done that, you can then sort the array. For example, using functional programming in Swift, you could do something like the following, which uses sorted to convert the dictionary to an array and then sort using the closure and then using DateFormatter to convert the strings to dates:

let dictionary = [
    "04-09-2015": 4,
    "04-10-2015": 6,
    "04-07-2015": 8,
    "03-28-2015": 10,
    "12-10-2014": 12,    
    "12-10-2015": 12
]

let formatter = DateFormatter()
formatter.dateFormat = "MM-dd-yyyy"

let result = dictionary.sorted {
    formatter.date(from: $0.0)! < formatter.date(from: $1.0)! 
}

While that enjoys a certain simplicity, it's probably a tad inefficient, calling date(from:) more than we need to. So, I'd probably have the map function add the Date object to the array of tuples, and then sorted can use that:

let result = dictionary.map { (formatter.date(from: $0)!, $0, $1) }
    .sorted { $0.0 < $1.0 }
    .map { ($0.1, $0.2) }

That final map discards the Date object, but clearly you don't need to do that if you're ok keeping the Date in the tuple.

like image 1
Rob Avatar answered Oct 18 '22 00:10

Rob