I have a dictionary from which I need to derive an Array of keys and an Array of values sorted EITHER on the keys or on the values.
My use case is a list of folders. The dictionary holds folder names (keys) and a count of items in the folders (values). I want to sort by key name (A to Z or Z to A) and also by count size (large to small or small to large).
Sorting the keys is easy in Swift. But I'm resorting to iteration to provide the sorted list of values. This does not seem like the Swift way of doing things, but my understanding of map / sort / etc. in Swift is not good enough for me to see a smarter way of doing this.
Can anyone explain a smart and concise Swift way of achieving this goal?
My current code is:
let dictionary = ["Alpha" : 24, "Beta" : 47, "Gamma" : 12, "Delta" : 33]
enum FolderOrder : Int {
case AlphaAtoZ
case AlphaZtoA
case SizeLargeToSmall
case SizeSmallToLarge
}
func folderList(fromDictionary: [String: Int], orderedBy : FolderOrder = .AlphaAtoZ) -> [String] {
switch orderedBy {
case .AlphaAtoZ:
return fromDictionary.keys.sort() {$0 < $1}
case .AlphaZtoA:
return fromDictionary.keys.sort() {$1 < $0}
case .SizeSmallToLarge:
return fromDictionary.keys.sort(){fromDictionary[$0] < fromDictionary [$1]}
case .SizeLargeToSmall:
return fromDictionary.keys.sort(){fromDictionary[$1] < fromDictionary [$0]}
}
}
func folderCounts(fromDictionary: [String: Int], orderedBy : FolderOrder = .AlphaAtoZ) -> [Int]
{
let orderedKeys = folderList(fromDictionary, orderedBy: orderedBy)
var orderedValues = [Int]()
for key in orderedKeys {
orderedValues.append(fromDictionary[key] ?? 0)
}
return orderedValues
}
folderList(dictionary, orderedBy: .AlphaAtoZ)
// ["Alpha", "Beta", "Delta", "Gamma"]
folderList(dictionary, orderedBy: .AlphaZtoA)
// ["Gamma", "Delta", "Beta", "Alpha"]
folderList(dictionary, orderedBy: .SizeSmallToLarge)
// ["Gamma", "Alpha", "Delta", "Beta"]
folderList(dictionary, orderedBy: .SizeLargeToSmall)
//["Beta", "Delta", "Alpha", "Gamma"]
folderCounts(dictionary, orderedBy: .AlphaAtoZ)
// [24, 47, 33, 12]
folderCounts(dictionary, orderedBy: .SizeLargeToSmall)
// [47, 33, 24, 12]
Update
Thanks to two helpful answers, but especially @nRewik, I've streamlined my code and improved my understanding of Swift.
Revised code, with comments spelling out what wasn't initially clear to me and so may be helpful to others:
let dictionary = ["Alpha" : 24, "Beta" : 47, "Gamma" : 12, "Delta" : 33]
enum FolderOrder {
case AlphaAtoZ
case AlphaZtoA
case SizeLargeToSmall
case SizeSmallToLarge
}
func folderListAndCounts(fromDictionary: [String: Int], orderedBy : FolderOrder = .AlphaAtoZ) -> ([String], [Int]) {
var sortedDictionary : [(String, Int)]
switch orderedBy {
// The closure when sort() is applied to a dictionary takes two tuples as parameters
// where the tuples are of the form (key, value). The first tuple can be accessed as $0.
// Its key can be accessed as $0.0 and its value as $0.1
case .AlphaAtoZ:
sortedDictionary = fromDictionary.sort{ $0.0 < $1.0 } // item(n).key < item(n+1).key
case .AlphaZtoA:
sortedDictionary = fromDictionary.sort{ $0.0 > $1.0 } // item(n).key > item(n+1).key
case .SizeSmallToLarge:
sortedDictionary = fromDictionary.sort{ $0.1 < $1.1 } // item(n).value < item(n+1).value
case .SizeLargeToSmall:
sortedDictionary = fromDictionary.sort{ $0.1 > $1.1 } // item(n).value < item(n+1).value
}
// The sorted dictionary has the type: [(String, Int)], i.e. it's an array of tuples.
// The closure when map is applied to an array of tuples is a tuple. The tuple can be
// accessed as $0. Its key can be accessed as $0.0 and its value as $0.1
let sortedKeys = sortedDictionary.map{$0.0}
let sortedValues = sortedDictionary.map{$0.1}
// Returns a tuple (arrayOfKeys, arrayOfValues)
return (sortedKeys, sortedValues)
}
let (keys, counts) = folderListAndCounts(dictionary, orderedBy: .SizeSmallToLarge)