virtualeyes
virtualeyes

Reputation: 11237

Map groupBy with multi-sort

Going a bit nutty here trying to multi-sort on the following.

case class WeeklyResults(
  schedule: Schedule,
  result: GameResult
)
val games = // returns correctly sorted List of WeeklyResults
  repo.gameresult.findAllByDate(date)

Things go awry when I groupBy on game date (to display game date header per game day) and id (to group home/away team pairs) as an unsorted map is returned, fun ;-)

val unsorted = // Map[JodaTime, Iterable[List[WeeklyResults]]]
  games.groupBy(_.schedule.gameDate).mapValues(_.groupBy(_.schedule.id).values)

Ok, well, ListMap is one way to get a sorted map, let's try that, sorting on game date JodaTime in milliseconds.

val sorted =
  ListMap(unsorted.toList.sortBy(_._1.getMillis):_*)

All good, games are sorted under game date headers in correct order...but, the games within each game day are sorted at random ;-(

So, the question is, how on earth can I sort both by game date and game result id? (where id is PK of game results table, effectively the sort order at DB level)

I have tried various combinations of futility:

unsorted.flatMap{x=>
  ListMap(
    Seq( (x._1, x._2.map(_._2.sortBy(_.result.id))) ).sortBy(_.1.getMillis)
  :_*)
}

No matter what I do, the Iterable[List[WeeklyResults]] remains unsorted

Ideas greatly appreciated, am at my wits end, entire morning gone on this

Upvotes: 2

Views: 1716

Answers (2)

Or Peles
Or Peles

Reputation: 475

I think the key problem is that your groupBy inside a groupBy returns a complex type (Iterable over List).

If I simplify that part using 'flatten', things become much simpler. I've used a dumbed-down version of your case class

case class WeeklyResults(
  schedule: DateTime,
  id: Int
)

and then used the following:

val unsorted = // Map[JodaTime, List[WeeklyResults]]
    games.groupBy(_.schedule).mapValues(_.groupBy(_.id).values.flatten.toList)

sorted just like you did (cool trick with the ListMap, by the way):

val sorted = //ListMap[DateTime, List[WeeklyResults] ]
    ListMap(unsorted.toList.sortBy(_._1.getMillis):_*)

and then the secondary sort is just:

val reallySorted = sorted.mapValues( v => v.sortBy( _.id) )

I hope that helps.

Upvotes: 2

virtualeyes
virtualeyes

Reputation: 11237

This is heinously bad, but not sure how else to approach the problem without restructuring the model.

So, I pass in the semi-sorted ListMap (i.e. sorted on game date) to the view:

@(model: Map[org.joda.time.DateTime, Iterable[List[ushr.model.WeeklyResults]]])

@model.map{ case(date,games) =>
  // display game date headers
  ...

  // the ugliness ensues
  @games.toSeq.map(x=>x).sortBy(_(0).result.id).map{ case(List(a,b))=>
    // display game results on this particular game date
    ...
  }
}

Basically, since each gameresult is a List'd pair, I sort on the first game result id in the pair (could do on the 2nd just as well, same game id, different game result/outcome for each team).

This works, but is really painful given that the original database query returns the result set in desired order. Spent about 3 hours hashing through this, interesting but...a total waste of the little time I have available right now. Would ABSOLUTELY LOVE a groupBy that preserved collection order.

If someone has a "real" solution vs. the hacked one I've come up with, please do chime in...

Upvotes: 0

Related Questions