CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scala-lang-modules--scala-collection-compat

A library that makes some Scala 2.13 APIs available on Scala 2.11 and 2.12, facilitating cross-building Scala 2.13 and 3.0 code on older versions

Pending
Overview
Eval results
Files

collection-extensions.mddocs/

Collection Extensions

Enhanced collection operations including safe min/max methods, advanced transformations, and functional programming utilities.

Safe Min/Max Operations

TraversableOnceExtensionMethods

implicit class TraversableOnceExtensionMethods[A](private val self: TraversableOnce[A])
    extends AnyVal {
  def minOption[B >: A](implicit ord: Ordering[B]): Option[A]
  def maxOption[B >: A](implicit ord: Ordering[B]): Option[A]
  def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
  def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
}

Safe versions of min/max operations that return Option instead of throwing exceptions on empty collections.

minOption / maxOption

def minOption[B >: A](implicit ord: Ordering[B]): Option[A]
def maxOption[B >: A](implicit ord: Ordering[B]): Option[A]

Find the minimum or maximum element in a collection.

Returns:

  • Some(element) if the collection is non-empty
  • None if the collection is empty

Usage:

List(3, 1, 4, 1, 5).minOption  // Some(1)
List(3, 1, 4, 1, 5).maxOption  // Some(5)
List.empty[Int].minOption      // None
List.empty[Int].maxOption      // None

minByOption / maxByOption

def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]

Find the element that produces the minimum or maximum value when the function is applied.

Usage:

case class Person(name: String, age: Int)
val people = List(Person("Alice", 25), Person("Bob", 30), Person("Charlie", 20))

people.minByOption(_.age)  // Some(Person("Charlie", 20))
people.maxByOption(_.age)  // Some(Person("Bob", 30))
List.empty[Person].minByOption(_.age)  // None

Advanced Collection Transformations

TraversableLikeExtensionMethods

implicit class TraversableLikeExtensionMethods[A, Repr](
    private val self: GenTraversableLike[A, Repr]) extends AnyVal {
  def tapEach[U](f: A => U)(implicit bf: CanBuildFrom[Repr, A, Repr]): Repr
  def partitionMap[A1, A2, That, Repr1, Repr2](f: A => Either[A1, A2]): (Repr1, Repr2)
  def groupMap[K, B, That](key: A => K)(f: A => B): Map[K, That]
  def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): Map[K, B]
  def distinctBy[B, That](f: A => B): That
}

tapEach

def tapEach[U](f: A => U)(implicit bf: CanBuildFrom[Repr, A, Repr]): Repr

Apply a function to each element for its side effects and return the original collection unchanged.

Usage:

List(1, 2, 3)
  .tapEach(println)  // prints 1, 2, 3
  .map(_ * 2)        // List(2, 4, 6)

partitionMap

def partitionMap[A1, A2, That, Repr1, Repr2](f: A => Either[A1, A2]): (Repr1, Repr2)

Partition a collection into two collections based on a function that returns Either.

Usage:

val numbers = List(1, 2, 3, 4, 5, 6)
val (evens, odds) = numbers.partitionMap { n =>
  if (n % 2 == 0) Left(n) else Right(n)
}
// evens: List(2, 4, 6)
// odds: List(1, 3, 5)

groupMap

def groupMap[K, B, That](key: A => K)(f: A => B): Map[K, That]

Group elements by a key function and transform them with a mapping function in one operation.

Usage:

case class Student(name: String, grade: Char, score: Int)
val students = List(
  Student("Alice", 'A', 95),
  Student("Bob", 'B', 85),
  Student("Charlie", 'A', 92)
)

val scoresByGrade = students.groupMap(_.grade)(_.score)
// Map('A' -> List(95, 92), 'B' -> List(85))

groupMapReduce

def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): Map[K, B]

Group elements by a key, transform them, and reduce the values in each group.

Usage:

val students = List(
  Student("Alice", 'A', 95),
  Student("Bob", 'B', 85),  
  Student("Charlie", 'A', 92)
)

val avgScoreByGrade = students.groupMapReduce(_.grade)(_.score)(_ + _)
// Map('A' -> 187, 'B' -> 85)  // Note: sum, not average

distinctBy

def distinctBy[B, That](f: A => B): That

Remove duplicate elements based on the result of a key function.

Usage:

case class Person(name: String, age: Int)
val people = List(
  Person("Alice", 25),
  Person("Bob", 30),
  Person("Alice", 26)  // duplicate name
)

val uniqueByName = people.distinctBy(_.name)
// List(Person("Alice", 25), Person("Bob", 30))

Size Comparison Operations

SizeCompareOps

class SizeCompareOps(private val it: Traversable[_]) extends AnyVal {
  def <(size: Int): Boolean
  def <=(size: Int): Boolean
  def ==(size: Int): Boolean
  def !=(size: Int): Boolean
  def >=(size: Int): Boolean
  def >(size: Int): Boolean
}

Efficient size comparison operations that can short-circuit for better performance with large collections.

Usage:

val list = List(1, 2, 3, 4, 5)

// Size comparisons
list.sizeIs < 10     // true
list.sizeIs >= 5     // true
list.sizeIs == 5     // true
list.sizeIs != 3     // true

// Sequence length comparisons  
val seq = Seq(1, 2, 3)
seq.lengthIs > 2     // true
seq.lengthIs <= 5    // true

Collection Utility Extensions

TraversableExtensionMethods

implicit class TraversableExtensionMethods[A](private val self: Traversable[A])
    extends AnyVal {
  def iterableFactory: GenericCompanion[Traversable]
  def sizeCompare(otherSize: Int): Int
  def sizeIs: SizeCompareOps
  def sizeCompare(that: Traversable[_]): Int
}

sizeCompare

def sizeCompare(otherSize: Int): Int
def sizeCompare(that: Traversable[_]): Int

Compare the size of a collection with an integer or another collection efficiently.

Returns:

  • Negative value if collection is smaller
  • Zero if sizes are equal
  • Positive value if collection is larger

Usage:

val list1 = List(1, 2, 3)
val list2 = List(4, 5)

list1.sizeCompare(5)     // negative (3 < 5)
list1.sizeCompare(3)     // zero (3 == 3)
list1.sizeCompare(list2) // positive (3 > 2)

SeqExtensionMethods

implicit class SeqExtensionMethods[A](private val self: Seq[A]) extends AnyVal {
  def lengthIs: SizeCompareOps
}

Size comparison operations specifically for sequences.

Complete Usage Examples

Processing Data with Safe Operations

import scala.collection.compat._

case class Sale(product: String, amount: Double, region: String)
val sales = List(
  Sale("laptop", 999.99, "north"),
  Sale("mouse", 29.99, "south"),
  Sale("laptop", 899.99, "north"),
  Sale("keyboard", 79.99, "south")
)

// Safe aggregations
val maxSale = sales.maxByOption(_.amount)  // Some(Sale("laptop", 999.99, "north"))
val minSale = sales.minByOption(_.amount)  // Some(Sale("mouse", 29.99, "south"))

// Group and aggregate
val totalByRegion = sales.groupMapReduce(_.region)(_.amount)(_ + _)
// Map("north" -> 1899.98, "south" -> 109.98)

// Unique products by region
val productsByRegion = sales.groupMap(_.region)(_.product)
  .view.mapValues(_.distinctBy(identity)).toMap

// Process with side effects
val processedSales = sales
  .tapEach(sale => println(s"Processing ${sale.product}"))
  .filter(_.amount > 50)

Functional Processing Pipeline

def processNumbers(numbers: List[Int]): Map[String, List[Int]] = {
  numbers
    .tapEach(n => if (n < 0) println(s"Warning: negative number $n"))
    .partitionMap { n =>
      if (n % 2 == 0) Left(("even", n)) else Right(("odd", n))
    } match {
      case (evens, odds) => 
        Map("even" -> evens.map(_._2), "odd" -> odds.map(_._2))
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-scala-lang-modules--scala-collection-compat

docs

annotation-backports.md

backported-collections.md

collection-extensions.md

collection-factories.md

index.md

iterator-size-ops.md

java-interop.md

map-extensions.md

method-chaining.md

option-converters.md

resource-management.md

string-parsing.md

tile.json