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
—
Enhanced map operations for both immutable and mutable maps, providing additional utility methods and conditional update operations.
implicit class MapExtensionMethods[K, V](private val self: Map[K, V]) extends AnyVal {
def foreachEntry[U](f: (K, V) => U): Unit
}def foreachEntry[U](f: (K, V) => U): UnitIterate over key-value pairs with a function that takes separate key and value parameters instead of a tuple.
Usage:
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
// Traditional foreach with tuple
map.foreach { case (k, v) => println(s"$k: $v") }
// foreachEntry with separate parameters
map.foreachEntry { (k, v) => println(s"$k: $v") }implicit class ImmutableMapExtensionMethods[K, V](
private val self: immutable.Map[K, V]) extends AnyVal {
def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): Map[K, V1]
}def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): Map[K, V1]Conditionally update a map entry based on the current value. The remapping function receives the current value as an Option and returns the new value as an Option.
Behavior:
Some(value), the key is updated with the new valueNone, the key is removed from the mapSome(currentValue) if the key exists, None otherwiseUsage:
val map = Map("a" -> 1, "b" -> 2)
// Increment existing value or set to 1 if not present
val incremented = map.updatedWith("a") {
case Some(value) => Some(value + 1)
case None => Some(1)
}
// Result: Map("a" -> 2, "b" -> 2)
// Remove key conditionally
val filtered = map.updatedWith("a") {
case Some(value) if value > 1 => None // Remove if > 1
case other => other // Keep as is
}
// Result: Map("b" -> 2)
// Add new key
val withNew = map.updatedWith("c") {
case Some(value) => Some(value + 10)
case None => Some(100)
}
// Result: Map("a" -> 1, "b" -> 2, "c" -> 100)implicit class MutableMapExtensionMethods[K, V](
private val self: mutable.Map[K, V]) extends AnyVal {
def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V]
}def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V]Conditionally update a mutable map entry in-place. Similar to updatedWith but modifies the map directly and returns the final value.
Returns: The final value associated with the key after the update, or None if the key was removed.
Usage:
val map = mutable.Map("a" -> 1, "b" -> 2)
// Increment existing value or set to 1 if not present
val result1 = map.updateWith("a") {
case Some(value) => Some(value + 1)
case None => Some(1)
}
// map is now Map("a" -> 2, "b" -> 2)
// result1 is Some(2)
// Remove key conditionally
val result2 = map.updateWith("a") {
case Some(value) if value > 1 => None
case other => other
}
// map is now Map("b" -> 2)
// result2 is None
// Add new key
val result3 = map.updateWith("c") {
case Some(value) => Some(value + 10)
case None => Some(100)
}
// map is now Map("b" -> 2, "c" -> 100)
// result3 is Some(100)implicit class MapViewExtensionMethods[K, V, C <: Map[K, V]](
private val self: IterableView[(K, V), C]) extends AnyVal {
def mapValues[W, That](f: V => W): That
def filterKeys(p: K => Boolean): IterableView[(K, V), C]
}Extensions for map views that provide lazy transformation operations.
def mapValues[W, That](f: V => W): ThatTransform the values in a map view lazily.
Usage:
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
val view = map.view
val doubled = view.mapValues(_ * 2)
// Lazy view - values not computed until accessed
doubled.force // Map("a" -> 2, "b" -> 4, "c" -> 6)def filterKeys(p: K => Boolean): IterableView[(K, V), C]Filter map entries by keys in a lazy view.
Usage:
val map = Map("apple" -> 1, "banana" -> 2, "apricot" -> 3)
val view = map.view
val aWords = view.filterKeys(_.startsWith("a"))
// Lazy view - filtering not applied until accessed
aWords.force // Map("apple" -> 1, "apricot" -> 3)import scala.collection.compat._
case class CacheEntry[T](value: T, timestamp: Long)
class Cache[K, V] {
private var store = Map.empty[K, CacheEntry[V]]
private val ttlMs = 60000 // 1 minute TTL
def get(key: K): Option[V] = {
val now = System.currentTimeMillis()
store.get(key) match {
case Some(entry) if now - entry.timestamp < ttlMs =>
Some(entry.value)
case _ =>
store = store.updatedWith(key)(_ => None) // Remove expired entry
None
}
}
def put(key: K, value: V): Unit = {
val now = System.currentTimeMillis()
store = store.updatedWith(key)(_ => Some(CacheEntry(value, now)))
}
}import scala.collection.{mutable, compat}
import compat._
val stats = mutable.Map.empty[String, Int]
def recordEvent(event: String): Unit = {
stats.updateWith(event) {
case Some(count) => Some(count + 1)
case None => Some(1)
}
}
recordEvent("login") // stats: Map("login" -> 1)
recordEvent("logout") // stats: Map("login" -> 1, "logout" -> 1)
recordEvent("login") // stats: Map("login" -> 2, "logout" -> 1)case class Config(timeout: Int, retries: Int, enabled: Boolean)
val baseConfig = Map(
"api" -> Config(5000, 3, true),
"db" -> Config(30000, 1, true)
)
// Disable a service conditionally
val updatedConfig = baseConfig.updatedWith("api") {
case Some(config) if config.timeout > 10000 => Some(config.copy(enabled = false))
case other => other
}
// Add new service configuration
val withNewService = updatedConfig.updatedWith("cache") {
case Some(existing) => Some(existing) // Keep existing
case None => Some(Config(1000, 5, true)) // Add new
}def processUserPreferences(
current: Map[String, String],
updates: List[(String, Option[String])]
): Map[String, String] = {
updates.foldLeft(current) { case (map, (key, valueOpt)) =>
map.updatedWith(key)(_ => valueOpt)
}
}
val prefs = Map("theme" -> "dark", "lang" -> "en")
val updates = List(
"theme" -> Some("light"), // Update existing
"lang" -> None, // Remove existing
"region" -> Some("us") // Add new
)
val result = processUserPreferences(prefs, updates)
// Result: Map("theme" -> "light", "region" -> "us")val inventory = Map(
"laptop" -> 999.99,
"mouse" -> 29.99,
"keyboard" -> 79.99,
"monitor" -> 299.99
)
// Chain view operations for efficient processing
val expensiveItems = inventory.view
.filterKeys(!_.contains("mouse")) // Exclude mice
.mapValues(_ * 1.1) // Add 10% markup
.filter(_._2 > 100) // Only expensive items
.toMap
// Result: Map("laptop" -> 1099.989, "monitor" -> 329.989)updatedWith creates a new immutable map, preserving the originalupdateWith modifies the mutable map in-place and returns the result valueMap.compute methodsInstall with Tessl CLI
npx tessl i tessl/maven-org-scala-lang-modules--scala-collection-compat