Víctor Gonzal
Víctor Gonzal

Reputation: 141

Sort a dictionary contained in an array

Hello I have a dictionary contained in an array. Something like this:

var array = [[String: Any]] ()

After executing a certain code, the array is filled with the following example data:

array = [["Date": "Sat-16-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30" ]]
["Date": "Thu-14-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]
["Date": "Fri-15-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]
["Date": "Mon-11-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]
["Date": "Sun-10-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]
["Date": "Wed-13-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]
["Date": "Sat-09-May", "sched": ["8:00 - 8:30", "8:30 - 9:00", "09:00 - 09:30"]]

]

What I need is to be able to sort them by day of the week. Because he always prints it to me in a different order

I have found solutions to sort dictionaries, but not to sort dictionaries contained in an array. How can I access the dictionary key (Date) to order them according to that data?

Thank you :)

Upvotes: 0

Views: 68

Answers (2)

joseph0x4365732E
joseph0x4365732E

Reputation: 151

Note

First of all, the example data has a few inconsistencies in the date formatting, as mentioned by @Larme. This answer assumes that the example data was intended as below, and adds "AM":

array = [
    ["Date": "Sat-16-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Thu-14-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Fri-15-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Mon-11-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Sun-10-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Wed-13-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Sat-09-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]]
]

Best Practice

Your example data indicates that you are storing durations for dates. The best way to sort these is by using [Date: [DateInterval]]. When you want to display these dates and durations as strings, you can use a DateFormatter for the Date's and a DateIntervalFormatter for the DateInterval's. This data can be stored as a Dictionary and sorted into an Array of tuples with the keys and values, [(key: Date, value: [DateInterval])].

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "eee-dd-MMM"

let intervalFormatter = DateIntervalFormatter()
intervalFormatter.dateStyle = .none
intervalFormatter.timeStyle = .short

var newDict = [Date: [DateInterval]]()

The sorted Array:

let arrayOfTuples = newDict.sorted { (left, right) -> Bool in
    left.key > right.key
}

Any time you need to display one of these dates or intervals, you can simply use:

dateFormatter.string(from: aDate) // replace aDate
intervalFormatter.string(from: aDateInterval) // replace aDateInterval

If you want to use your example data as a literal for testing or otherwise, you can convert it to [Date: [DateInterval]] using this code:

let disposableFormatter = DateFormatter()
disposableFormatter.dateFormat = "eee-dd-MMMhh:mm a"
    
array.forEach { dictionaryPiece in
    let dateString = dictionaryPiece["Date"] as! String
    let date = dateFormatter.date(from: dateString)!
    let sched = dictionaryPiece["sched"] as! [String]
    newDict[date] =
        sched.map { (stringInterval: String) -> DateInterval in
            let start = disposableFormatter.date(from: dateString + stringInterval.components(separatedBy: " - ")[0])!
            let end = disposableFormatter.date(from: dateString + stringInterval.components(separatedBy: " - ")[1])!
            return DateInterval(start: start, end: end)
        }
}

The Other Way

Finally, if you simply want to sort your data exactly as it is, you can just use Array.sort(by areInIncreasingOrder: (Element, Element) throws -> Bool), as implemented below.

var array = [[String: Any]] ()

array = [
    ["Date": "Sat-16-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Thu-14-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Fri-15-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Mon-11-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Sun-10-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Wed-13-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]],
    ["Date": "Sat-09-May", "sched": ["08:00 AM - 08:30 AM", "08:30 AM - 09:00 AM", "09:00 AM - 09:30 AM"]]
]

let formatter = DateFormatter()
formatter.dateFormat = "eee-dd-MMM"

array.sort { (left, right) -> Bool in
    formatter.date(from: left["Date"] as! String)! > formatter.date(from: right["Date"] as! String)!
}

Upvotes: 1

Gereon
Gereon

Reputation: 17844

Taking your data structure as-is, you can sort your array(!) like this

var arr = [[String: Any]] ()
arr.sort { (dict1, dict2) -> Bool in
    let date1 = dict1["Date"] as? String ?? ""
    let date2 = dict2["Date"] as? String ?? ""
    return date1 < date2
}

That said, if at all possible, you should avoid stringly typed APIs that rely on [String: Any] dictionaries and the resulting casting and optional checking hassle.

Upvotes: 0

Related Questions