Reputation: 3922
I lately encountered this question: JPA: How do I sort on a Set field in my entity?
I started to think how similar sorting can be done in Scala.
So we have a list of users. Every user has sorted list of organizations. And we want to sort this list of users by list of organization names. Users are sorted by first organization name and when first names are equal then they are compared by second names and so on.
I have managed to write such sorting but in some cases it is giving wrong result.
class Organization (aId: Int, aName: String) {
val id:Int = aId
var name:String = aName
}
class User (aId: Int, aOrganizations: List[Organization]) {
val id:Int = aId
var organizations:List[Organization] = aOrganizations
}
val o1 = new Organization(1, "AAA")
val o2 = new Organization(2, "AAAA")
val o3 = new Organization(3, "BBB")
val o4 = new Organization(4, "BBBB")
val o5 = new Organization(5, "CCC")
val o6 = new Organization(6, "AAA BBB")
val u1 = new User(1, List(o1))
val u2 = new User(2, List(o2))
val u3 = new User(3, List(o3))
val u4 = new User(4, List(o4))
val u5 = new User(5, List(o1,o5))
val u6 = new User(6, List(o2,o3))
val u7 = new User(7, List(o3,o4))
val u8 = new User(8, List(o1,o2,o3,o4))
val u9 = new User(9, List(o1,o2,o3,o5))
val u10 = new User(10, List())
val u11 = new User(11, List(o6))
val users = List(u1, u2, u3, u4, u5, u6, u7, u8, u9, u10, u11)
// below line should be improved
val sortedUsers = users.sortWith(_.organizations.foldLeft("")((b,a) => b + a.name + ",") < _.organizations.foldLeft("")((b,a) => b + a.name + ","))
sortedUsers.foreach{ x => print(x.id+" ")}
// received result: 10 11 1 8 9 5 2 6 3 7 4
// expected result: 10 1 8 9 5 11 2 6 3 7 4
How can such sorting be done?
Upvotes: 0
Views: 147
Reputation: 139038
The most straightforward way to solve this is probably to use sortBy
instead of sortWith
. Scala provides a lexicographic ordering instance for Iterable[A]
where A
has an ordering instance, so you just need to provide an ordering instance for Organization
:
implicit val organizationOrdering: Ordering[Organization] =
Ordering.by(_.name)
val sortedUsers = users.sortBy(_.organizations.toIterable)
You could also just provide an instance for User
and then use sorted
:
implicit val organizationOrdering: Ordering[Organization] =
Ordering.by(_.name)
implicit val userOrdering: Ordering[User] =
Ordering.by(_.organizations.toIterable)
val sortedUsers = users.sorted
If you don't want to introduce these instances, you can pass the one you need explicitly:
val sortedUsers = users.sortBy(_.organizations.toIterable)(
Ordering.Iterable(Ordering.by(_.name))
)
It's a shame that there's not an instance for List[A: Ordering]
, but there are apparently good reasons for that.
Upvotes: 3