This one's a little goofy!
Based on this question , I'm re-ordering an array of objects based on indices in another array:
// pop this in a playground:
struct DataObject {
let id: String
let name: String
let isRequired: Bool
init(_ id: String, _ name: String, _ isRequired: Bool) {
self.id = id
self.name = name
self.isRequired = isRequired
}
}
struct User {
let sortingIndex: [String]
init(_ sortingIndex: [String]) {
self.sortingIndex = sortingIndex
}
}
let a = [DataObject("1", "A", false), DataObject("2", "B", true), DataObject("3", "C", true), DataObject("4", "D", false)]
func sort(a: [DataObject], forUser user: User) -> [DataObject] {
return a.sort { user.sortingIndex.indexOf($0.id) < user.sortingIndex.indexOf($1.id) }
}
let user = User(["1", "2", "4", "3"])
sort(a, forUser: user)
The output look like this:
[{id "3", name "C", isRequired true}, {id "2", name "B", isRequired true}, {id "1", name "A", isRequired false}, {id "4", name "D", isRequired false}]
(mostly good!)
I want to prioritize the isRequired
ones to be at the beginning of the array in alphabetical order, so I tried:
return a.sort { $0.isRequired ? $0.name < $1.name : user.sortingIndex.indexOf($0.id) < user.sortingIndex.indexOf($1.id) }
\\ ^
(which does nothing)
or:
return a.sort { $0.isRequired ? $0.name > $1.name : user.sortingIndex.indexOf($0.id) < user.sortingIndex.indexOf($1.id) }
\\ ^
(which works, but it's in reverse alphabetical order)
I want it to look like this in the end:
[
{id "2", name "B", isRequired true},
{id "3", name "C", isRequired true},
{id "1", name "A", isRequired false},
{id "4", name "D", isRequired false}
]
When only one of $0
or $1
is required, you should always return true
or false
respectively, regardless of the name. So something like this would work:
return a.sort {
if $0.isRequired && $1.isRequired {
return $0.name < $1.name
}
if $0.isRequired { return true }
if $1.isRequired { return false }
return user.sortingIndex.indexOf($0.id) < user.sortingIndex.indexOf($1.id)
}
Change your sort to:
a.sort { (lhs, rhs) -> Bool in
if lhs.isRequired != rhs.isRequired { return lhs.isRequired }
if lhs.name != hrs.name {return lhs.name < rhs.name}
return user.sortingIndex.indexOf(lhs.id) < user.sortingIndex.indexOf(rhs.id)
}
Check the isRequired first, then check the name, and finally check the user index.
Here's a cute way of doing it :
return a.sort({ $0.isRequired != $1.isRequired ? $0.isRequired
: $0.isRequired ? $0.name < $1.name
: user.sortingIndex.indexOf($0.id) < user.sortingIndex.indexOf($1.id)
})
Yours is a varying level sort but, in a more general way, multi level sort conditions always have the same pattern :
$0.level1 != $1.level1 ? $0.level1 < $1.level1
: $0.level2 != $1.level2 ? $0.level2 < $1.level2
...
: $0.levelN-1 != $1.levelN-1 ? $0.levelN-1 < $1.levelN-1
: $0.levelN < $1.levelN
You could define the >
operator for Bool
values and use that to sort the objects array:
func >(lhs: Bool, rhs: Bool) -> Bool {
return lhs && !rhs
}
let sortedObjects = sort(a, forUser: user).sort{$0.isRequired > $1.isRequired || $0.name < $1.name}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.