rysv
rysv

Reputation: 3400

Scala slick query not sorting

I have this Scala code but it doesn't give the sorted list:

def getItemsByCategoryId(catId: Long, start: Int = 0, limit: Option[Int] = None): Future[Seq[(Item, Seq[String])]] = {

  val itemList = items.sortBy(_.name).filter(_.categoryId === catId).drop(start)

  val q = for {
    (j, pair) <- itemList joinLeft (groups join tags on (_.tagId === _.id)) on (_.id === _._1.itemId)
  } yield (j, pair)

  db.run(q.result).map { (row) =>
    row.groupBy(_._1).map { x =>
      val tags = x._2.map(_._2).flatten
      (x._1, tags.map(_._2.keyword))
    }.toSeq
  }
}

If I use only this line below I get the sorted list

val itemList = items.sortBy(_.name).filter(_.categoryId === catId).drop(start)

Is the join/groupBy operations affect the sort in some way?

Upvotes: 1

Views: 456

Answers (2)

Roman
Roman

Reputation: 5699

TraversableLike.groupBy returns an immutable.Map. Insertion order is preserved for Map values only, as the implementation iterates its elements in a for-loop. Keys in opposite do not really have an order. They are the result of the supplied function.

Scalas standard collection library does not have an out-of-the-box solution for this problem. Since I had exactly the same issue I wrote my own orderedGroupBy as an extension to Seq that returns an immutable.ListMap instead:

implicit class SeqWithOrderedGroupBy[A](xs: Seq[A]) {

  /**
    * Partitions this traversable collection into a map of traversable collections according to some discriminator function.
    * Preserves insertion order.
    *
    * @param f the discriminatior function.
    * @tparam K the type of keys returned by the discriminator function.
    * @return An ordered map from keys to seq.
    */
  def orderedGroupBy[K](f: A => K): immutable.ListMap[K, Seq[A]] = {
    val m = mutable.ListBuffer.empty[(K, mutable.Builder[A, Seq[A]])]
    for (elem <- xs) {
      val key = f(elem)
      val builder = m.find(_._1 == key)
        .map(_._2)
        .getOrElse {
          val bldr = mutable.Seq.newBuilder[A]
          m.append((key, bldr))
          bldr
        }

      builder += elem
    }
    val b = immutable.ListMap.newBuilder[K, Seq[A]]
    for ((k, v) <- m)
      b += ((k, v.result))

    b.result
  }
}

Disclaimer: I didn't compare performance of the above snippet to TraversableLike.groupBy. It is sufficient for my purposes but it might be worse. Any improvements are welcome though.

Upvotes: 1

Chetan Kumar Meena
Chetan Kumar Meena

Reputation: 354

GroupBy does not preserve ordering as it returns a map. This behaviour is consistent with Scala collections.

Upvotes: 2

Related Questions