Scala GroupBy сохранение порядка вставки?

Метод GroupBy в списках, картах и ​​т.д. генерирует карту после функции.

Есть ли способ использовать groupBy для создания Карты, которая сохраняет порядок вставки (например, LinkedHashMap)?

Я использую для циклов для вставки вручную, но я хотел знать, может ли помочь мне одна из полезных уже определенных функций.

Спасибо заранее.

Ответ 1

groupBy, как определено в TraversableLike, создает immutable.Map, поэтому вы не можете заставить этот метод произвести что-то еще.

Порядок элементов в каждой записи уже сохранен, но не порядок ключей. Ключи являются результатом предоставленной функции, поэтому у них действительно нет порядка.

Если вы хотите сделать заказ, основанный на первом вхождении конкретного ключа, здесь приведено описание того, как вы можете это сделать. Скажем, мы хотим сгруппировать целые числа по их значению /2:

val m = List(4, 0, 5, 1, 2, 6, 3).zipWithIndex groupBy (_._1 / 2)
val lhm = LinkedHashMap(m.toSeq sortBy (_._2.head._2): _*)
lhm mapValues (_ map (_._1))
// Map(2 -> List(4, 5), 0 -> List(0, 1), 1 -> List(2, 3), 3 -> List(6))
// Note order of keys is same as first occurrence in original list

Ответ 2

Следующее предоставит вам метод groupByOrdered, который ведет себя так, как вы искали.

import collection.mutable.{LinkedHashMap, LinkedHashSet, Map => MutableMap}

object GroupByOrderedImplicit {
  implicit class GroupByOrderedImplicitImpl[A](val t: Traversable[A]) extends AnyVal {
    def groupByOrdered[K](f: A => K): MutableMap[K, LinkedHashSet[A]] = {
      val map = LinkedHashMap[K,LinkedHashSet[A]]().withDefault(_ => LinkedHashSet[A]())
      for (i <- t) {
        val key = f(i)
        map(key) = map(key) + i
      }
      map
    }
  }
}

Когда я использую этот код, например:

import GroupByOrderedImplicit._
0.to(100).groupByOrdered(_ % 10).foreach(println)

Я получаю следующий вывод:

(0,Set(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100))
(1,Set(1, 11, 21, 31, 41, 51, 61, 71, 81, 91))
(2,Set(2, 12, 22, 32, 42, 52, 62, 72, 82, 92))
(3,Set(3, 13, 23, 33, 43, 53, 63, 73, 83, 93))
(4,Set(4, 14, 24, 34, 44, 54, 64, 74, 84, 94))
(5,Set(5, 15, 25, 35, 45, 55, 65, 75, 85, 95))
(6,Set(6, 16, 26, 36, 46, 56, 66, 76, 86, 96))
(7,Set(7, 17, 27, 37, 47, 57, 67, 77, 87, 97))
(8,Set(8, 18, 28, 38, 48, 58, 68, 78, 88, 98))
(9,Set(9, 19, 29, 39, 49, 59, 69, 79, 89, 99))

Ответ 3

Здесь один без карт:

def orderedGroupBy[T, P](seq: Traversable[T])(f: T => P): Seq[(P, Traversable[T])] = {
   @tailrec
   def accumulator(seq: Traversable[T], f: T => P, res: List[(P, Traversable[T])]): Seq[(P, Traversable[T])] = seq.headOption match {
     case None => res.reverse
     case Some(h) => {
       val key = f(h)
       val subseq = seq.takeWhile(f(_) == key)
       accumulator(seq.drop(subseq.size), f, (key -> subseq) :: res)
     }
   }
   accumulator(seq, f, Nil)
 }

Это может быть полезно, если вам нужно получить доступ к результатам последовательно (без произвольного доступа), и вы хотите избежать накладных расходов на создание и использование объектов Map. Примечание. Я не сравнивал производительность с другими параметрами, это действительно могло быть хуже.

EDIT: просто чтобы быть ясным; это предполагает, что ваш вход уже упорядочен групповым ключом. Моим вариантом использования является SELECT ... ORDER BY.

Ответ 4

    This yields better results on ScalaMeter though the solution is very similar to the actual scala groupBy
    ::Benchmark Range.GroupBy::
    cores: 8
    hostname: xxxxx-MacBook-Pro.local
    name: Java HotSpot(TM) 64-Bit Server VM
    osArch: x86_64
    osName: Mac OS X
    vendor: Oracle Corporation
    version: 25.131-b11
    Parameters(size -> 300000): 6.500884
    Parameters(size -> 600000): 13.019679
    Parameters(size -> 900000): 22.756615
    Parameters(size -> 1200000): 25.481007
    Parameters(size -> 1500000): 33.129888
    compared to the one that zipWithIndex approach which yields
    :Benchmark Range.GroupBy::
    cores: 8
    hostname: xxxxx-MacBook-Pro.local
    name: Java HotSpot(TM) 64-Bit Server VM
    osArch: x86_64
    osName: Mac OS X
    vendor: Oracle Corporation
    version: 25.131-b11
    Parameters(size -> 300000): 9.57414
    Parameters(size -> 600000): 18.569085
    Parameters(size -> 900000): 28.233822
    Parameters(size -> 1200000): 36.975254
    Parameters(size -> 1500000): 47.447057
    implicit class GroupBy[A](val t: TraversableOnce[A]) {
 def sortedGroupBy[K](f: A => K)(implicit ordering: Ordering[K]): immutable.SortedMap[K, ArrayBuffer[A]] = {
 val m = mutable.SortedMap.empty[K, ArrayBuffer[A]]
 for (elem <- t) {
 val key = f(elem)
 val bldr = m.getOrElseUpdate(key, mutable.ArrayBuffer[A]())
 bldr += elem
 }
 val b = immutable.SortedMap.newBuilder[K, ArrayBuffer[A]]
 for ((k, v) <- m) {
 b += ((k, v.result))
 }
 b.result
 }
 }
 example: val sizes = Gen.range("size")(300000, 1500000, 300000) and 
 groupByOrdered(_ % 10)