diff --git a/src/main/scala/scala/collection/next/NextIterableOnceOpsExtensions.scala b/src/main/scala/scala/collection/next/NextIterableOnceOpsExtensions.scala
index 2bd687d..c675f5c 100644
--- a/src/main/scala/scala/collection/next/NextIterableOnceOpsExtensions.scala
+++ b/src/main/scala/scala/collection/next/NextIterableOnceOpsExtensions.scala
@@ -16,6 +16,26 @@ package next
 private[next] final class NextIterableOnceOpsExtensions[A, CC[_], C](
   private val col: IterableOnceOps[A, CC, C]
 ) extends AnyVal {
+  import NextIterableOnceOpsExtensions.{GroupMapToView, GroupMapView}
+
+  def groupBy[K](key: A => K)(implicit groupsFactory: Factory[A, C]): immutable.Map[K, C] =
+    viewGroupByTo(key).toMap
+
+  def viewGroupByTo[K](key: A => K)(implicit groupsFactory: Factory[A, C]): GroupMapToView[A, K, A, C] =
+    viewGroupBy(key).collectGroupsTo(groupsFactory)
+
+  def viewGroupBy[K](key: A => K): GroupMapView[A, K, A] =
+    viewGroupMap(key)(identity)
+
+  def groupMap[K, V](key: A => K)(f: A => V)(implicit groupsFactory: Factory[V, CC[V]]): immutable.Map[K, CC[V]] =
+    viewGroupMapTo(key)(f).toMap
+
+  def viewGroupMapTo[K, V](key: A => K)(f: A => V)(implicit groupsFactory: Factory[V, CC[V]]): GroupMapToView[A, K, V, CC[V]] =
+    viewGroupMap(key)(f).collectGroupsTo(groupsFactory)
+
+  def viewGroupMap[K, V](key: A => K)(f: A => V): GroupMapView[A, K, V] =
+    new GroupMapView(col, key, f)
+
   /**
    * Partitions this IterableOnce into a map according to a discriminator function `key`. All the values that
    * have the same discriminator are then transformed by the `value` function and then reduced into a
@@ -28,14 +48,51 @@ private[next] final class NextIterableOnceOpsExtensions[A, CC[_], C](
    *
    * @note This will force the evaluation of the Iterator.
    */
-  def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): immutable.Map[K, B] = {
-    val m = mutable.Map.empty[K, B]
-    col.foreach { elem =>
-      m.updateWith(key = key(elem)) {
-        case Some(b) => Some(reduce(b, f(elem)))
-        case None    => Some(f(elem))
+  def groupMapReduce[K, V](key: A => K)(f: A => V)(reduce: (V, V) => V): immutable.Map[K, V] =
+    viewGroupMap(key)(f).reduceValuesTo(immutable.Map)(reduce)
+}
+
+private[next] object NextIterableOnceOpsExtensions {
+  final class GroupMapView[A, K, V] private[NextIterableOnceOpsExtensions](
+    col: IterableOnceOps[A, AnyConstr, _],
+    key: A => K,
+    f: A => V
+  ) {
+    def reduceValuesTo[MC](resultFactory: Factory[(K, V), MC])(reduce: (V, V) => V): MC = {
+      val m = mutable.Map.empty[K, V]
+      col.foreach { elem =>
+        m.updateWith(key = key(elem)) {
+          case Some(b) => Some(reduce(b, f(elem)))
+          case None    => Some(f(elem))
+        }
+      }
+      resultFactory.fromSpecific(m)
+    }
+
+    def collectGroupsTo[C](groupsFactory: Factory[V, C]): GroupMapToView[A, K, V, C] =
+      new GroupMapToView(col, key, f, groupsFactory)
+  }
+
+  final class GroupMapToView[A, K, V, C] private[NextIterableOnceOpsExtensions](
+    col: IterableOnceOps[A, AnyConstr, _],
+    key: A => K,
+    f: A => V,
+    groupsFactory: Factory[V, C]
+  ) {
+    def toMap: immutable.Map[K, C] =
+      to(immutable.Map)
+
+    def to[MC](resultFactory: Factory[(K, C), MC]): MC = {
+      val m = mutable.Map.empty[K, mutable.Builder[V, C]]
+      col.foreach { elem =>
+        val k = key(elem)
+        val v = f(elem)
+        m.get(k) match {
+          case Some(builder) => builder.addOne(v)
+          case None          => m.update(key = k, value = groupsFactory.newBuilder.addOne(v))
+        }
       }
+      resultFactory.fromSpecific(m.view.mapValues(_.result()))
     }
-    m.to(immutable.Map)
   }
 }
diff --git a/src/test/scala/scala/collection/next/TestIterableOnceExtensions.scala b/src/test/scala/scala/collection/next/TestIterableOnceExtensions.scala
index 0d8962a..d626ec5 100644
--- a/src/test/scala/scala/collection/next/TestIterableOnceExtensions.scala
+++ b/src/test/scala/scala/collection/next/TestIterableOnceExtensions.scala
@@ -16,49 +16,172 @@ import org.junit.Assert._
 import org.junit.Test
 import scala.collection.IterableOnceOps
 import scala.collection.generic.IsIterableOnce
+import scala.collection.immutable.{ArraySeq, BitSet, SortedMap, SortedSet}
 
 final class TestIterableOnceExtensions {
-  import TestIterableOnceExtensions.LowerCaseString
+  import TestIterableOnceExtensions._
 
+  // groupMapReduce --------------------------------------------
   @Test
   def iteratorGroupMapReduce(): Unit = {
-    def occurrences[A](coll: IterableOnce[A]): Map[A, Int] =
-      coll.iterator.groupMapReduce(identity)(_ => 1)(_ + _)
+    def occurrences[A](data: IterableOnce[A]): Map[A, Int] =
+      data.iterator.groupMapReduce(identity)(_ => 1)(_ + _)
 
-    val xs = Seq('a', 'b', 'b', 'c', 'a', 'a', 'a', 'b')
+    val data = Seq('a', 'b', 'b', 'c', 'a', 'a', 'a', 'b')
     val expected = Map('a' -> 4, 'b' -> 3, 'c' -> 1)
-    assertEquals(expected, occurrences(xs))
+
+    assertEquals(expected, occurrences(data))
   }
 
   @Test
   def iterableOnceOpsGroupMapReduce(): Unit = {
-    def occurrences[A, CC[_], C](coll: IterableOnceOps[A, CC, C]): Map[A, Int] =
-      coll.groupMapReduce(identity)(_ => 1)(_ + _)
+    def occurrences[A, CC[_], C](data: IterableOnceOps[A, CC, C]): Map[A, Int] =
+      data.groupMapReduce(identity)(_ => 1)(_ + _)
 
-    val xs = Seq('a', 'b', 'b', 'c', 'a', 'a', 'a', 'b')
+    val data = Seq('a', 'b', 'b', 'c', 'a', 'a', 'a', 'b')
     val expected = Map('a' -> 4, 'b' -> 3, 'c' -> 1)
-    assertEquals(expected, occurrences(xs))
+
+    assertEquals(expected, occurrences(data))
   }
 
   @Test
   def anyLikeIterableOnceGroupMapReduce(): Unit = {
-    def occurrences[Repr](coll: Repr)(implicit it: IsIterableOnce[Repr]): Map[it.A, Int] =
-      it(coll).iterator.groupMapReduce(identity)(_ => 1)(_ + _)
+    def occurrences[Repr](data: Repr)(implicit it: IsIterableOnce[Repr]): Map[it.A, Int] =
+      it(data).iterator.groupMapReduce(identity)(_ => 1)(_ + _)
 
-    val xs = "abbcaaab"
+    val data = "abbcaaab"
     val expected = Map('a' -> 4, 'b' -> 3, 'c' -> 1)
-    assertEquals(expected, occurrences(xs))
+
+    assertEquals(expected, occurrences(data))
   }
 
   @Test
   def customIterableOnceOpsGroupMapReduce(): Unit = {
-    def occurrences(coll: LowerCaseString): Map[Char, Int] =
-      coll.groupMapReduce(identity)(_ => 1)(_ + _)
+    def occurrences(data: LowerCaseString): Map[Char, Int] =
+      data.groupMapReduce(identity)(_ => 1)(_ + _)
 
-    val xs = LowerCaseString("abBcAaAb")
+    val data = LowerCaseString("abBcAaAb")
     val expected = Map('a' -> 4, 'b' -> 3, 'c' -> 1)
-    assertEquals(expected, occurrences(xs))
+
+    assertEquals(expected, occurrences(data))
+  }
+  // -----------------------------------------------------------
+
+  // GroupMapGenGen --------------------------------------------
+  @Test
+  def anyCollectionGroupMapToViewTo(): Unit = {
+    def getUniqueUsersByCountrySorted(data: List[Record]): List[(String, List[String])] =
+      data
+        .viewGroupMap(_.country)(_.user)
+        .collectGroupsTo(SortedSet)
+        .to(SortedMap)
+        .view
+        .mapValues(_.toList)
+        .toList
+
+    val data = List(
+      Record(user = "Luis", country = "Colombia"),
+      Record(user = "Seth", country = "USA"),
+      Record(user = "April", country =  "USA"),
+      Record(user = "Julien", country = "Suisse"),
+      Record(user = "Rob", country =  "USA"),
+      Record(user = "Seth", country = "USA")
+    )
+
+    val expected = List(
+      "Colombia" -> List("Luis"),
+      "Suisse" -> List("Julien"),
+      "USA" -> List("April", "Rob", "Seth")
+    )
+
+    assertEquals(expected, getUniqueUsersByCountrySorted(data))
+  }
+
+  @Test
+  def anyCollectionGroupMapViewReduceValuesTo(): Unit = {
+    def getAllWordsByFirstLetterSorted(data: List[String]): List[(Char, String)] =
+      data
+        .viewGroupBy(_.head)
+        .reduceValuesTo(SortedMap)(_ ++ " " ++ _)
+        .toList
+
+    val data = List(
+      "Autumn",
+      "Banana",
+      "April",
+      "Wilson",
+      "Apple",
+      "Apple",
+      "Winter",
+      "Banana"
+    )
+    val expected = List(
+      'A' -> "Autumn April Apple Apple",
+      'B' -> "Banana Banana",
+      'W' -> "Wilson Winter"
+    )
+
+    assertEquals(expected, getAllWordsByFirstLetterSorted(data))
+  }
+
+  @Test
+  def iterableOnceOpsViewGroupByToSpecificFactoryToMap(): Unit = {
+    def bitsByEven(data: BitSet): Map[Boolean, BitSet] =
+      data.viewGroupByTo(x => (x % 2) == 0).toMap
+
+    val data = BitSet(1, 2, 3, 4, 5)
+    val expected = Map(
+      true -> BitSet(2, 4),
+      false -> BitSet(1, 3, 5)
+    )
+
+    assertEquals(expected, bitsByEven(data))
+  }
+
+  @Test
+  def iterableOnceOpsViewGroupMapToIterableFactoryToMap(): Unit = {
+    def bitsByEvenAsChars(data: BitSet): Map[Boolean, Set[Char]] =
+      data.viewGroupMapTo(x => (x % 2) == 0)(_.toChar).toMap
+
+    val data = BitSet(100, 101, 102, 103, 104, 105)
+    val expected = Map(
+      true -> Set('d', 'f', 'h'),
+      false -> Set('e', 'g', 'i')
+    )
+
+    assertEquals(expected, bitsByEvenAsChars(data))
+  }
+
+  @Test
+  def iteratorGroupBy(): Unit = {
+    def getUniqueWordsByFirstLetter(data: IterableOnce[String]): List[(Char, Set[String])] =
+      data
+        .iterator
+        .groupBy(_.head)
+        .view
+        .mapValues(_.toSet)
+        .toList
+
+    val data = List(
+      "Autumn",
+      "Banana",
+      "April",
+      "Wilson",
+      "Apple",
+      "Apple",
+      "Winter",
+      "Banana"
+    )
+
+    val expected = List(
+      'A' -> Set("Apple", "April", "Autumn"),
+      'B' -> Set("Banana"),
+      'W' -> Set("Wilson", "Winter")
+    )
+
+    assertEquals(expected, getUniqueWordsByFirstLetter(data))
   }
+  // -----------------------------------------------------------
 }
 
 object TestIterableOnceExtensions {
@@ -81,4 +204,6 @@ object TestIterableOnceExtensions {
     override def span(p: Char => Boolean): (String, String) = ???
     override def tapEach[U](f: Char => U): String = ???
   }
+
+  final case class Record(user: String, country: String)
 }