I have a dictionary like eg:
let dict = Dictionary<Month, Array<Int32>>()
Obj1.price = "10"
Obj1.value = "abc"
Obj1.title = "January"
Obj2.price = "10"
Obj2.value = "def"
Obj2.title = "April"
Obj3.price = "10"
Obj3.value = "pqr"
Obj3.title = "February"
Obj4.price = "4"
Obj4.value = "mnq"
Obj4.title = "April"
dict = [ Obj1: [3,4], Obj2 : [1,2], Obj3: [8,9], Obj4: [3,3] ]
I have a custom array of month
let sortTemplate = ["April", "May", "June", "July", "August", "September", "October", "November", "December", "January", "February", "March"]
I want to get the dictionary sorted as [ Obj2 : [1,2], Obj4: [3,3], Obj1: [3,4], Obj3: [8,9] ]
In short I am expecting to sort the dictionary according to custom reference array on property of key. I know we cannot have sorted dictionary but want to sort according to custom sortTemplate and insert into Array of dictionaries
Any hint in this regard will be useful. I know we can sort with values and also keys
This is one possible solution which uses Dictionary
s built-in sort function, however presenting the title
property in your example as custom enum
rather than a String
. The "sort template" is then given implicitly by the ordering of the months in the enum
.
I.e., enum MonthSortTemplate
and your class MyClass
(the latter which you have not shower us, so I made a MWE myself) as:
enum MonthSortTemplate: Int {
case April = 1
case January
case February
// ... rest of months follows, in the order you prefer
}
class MyClass {
var price = ""
var value = ""
var title: MonthSortTemplate = .April
}
// Hashable (just some dummy, don't know how you've set this up)
extension MyClass: Hashable {
var hashValue: Int {
return price.hashValue ^ value.hashValue
}
}
// Equatable (just some dummy, don't know how you've set this up)
func ==(lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.price == rhs.price && lhs.value == rhs.value
}
Create your MyClass
instances, add into your dictionary, and use .sort(...)
function of the latter for a custom closure, specified for this specific type of comparison.
var Obj1 = MyClass()
var Obj2 = MyClass()
var Obj3 = MyClass()
Obj1.price = "10"
Obj1.value = "abc"
Obj1.title = .January
Obj2.price = "10"
Obj2.value = "def"
Obj2.title = .April
Obj3.price = "10"
Obj3.value = "pqr"
Obj3.title = .February
var dict = Dictionary<MyClass, Array<Int32>>()
dict = [ Obj1: [3,4], Obj2 : [1,2], Obj3: [8,9]]
// your custom sort closure, for Dictionary.sort(...) method
let byMonthTemplate = {
(elem1:(key: MyClass, val: [Int32]), elem2:(key: MyClass, val: [Int32]))->Bool in
if elem1.key.title.rawValue < elem2.key.title.rawValue {
return true
} else {
return false
}
}
let sortedDict = dict.sort(byMonthTemplate)
print("\(dict)")
An alternative---if your really like your class property title
to be of type String
---is to define the <
operator for MyClass
objects:
func <(lhs: MyClass, rhs: MyClass) -> Bool {
// do comparison stuff with strings lhs.title and rhs.title
// with regard to your ordering of choice (array sortTemplate)
return ...
}
In this case, the "messy" stuff ends up in this function, whereas the actual sorting can be performed quite elegantly as
let sortedDict = dict.sort { $0.0 < $1.0 }
Personally, I would prefer the enum
solution (that, however, is off topic).
EDIT:
At your request, I'll include one example of the <
operator for your class MyClass
. This is by no means an optimal one, but perhaps you can refine it from my example.
// add sortTemplate as a static property of MyClass
class MyClass {
var price = ""
var value = ""
var title = ""
static let sortTemplate = ["April", "May", "June", "July", "August", "September", "October", "November", "December", "January", "February", "March"]
}
// define < operator for MyClass objects
func <(lhs: MyClass, rhs: MyClass) -> Bool {
let indexOfLhs = MyClass.sortTemplate.indexOf({$0 == lhs.title})
let indexOfRhs = MyClass.sortTemplate.indexOf({$0 == rhs.title})
return indexOfLhs < indexOfRhs
}
// you can now sort your dictionary according to
let sortedDict = dict.sort { $0.0 < $1.0 }