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

map-extensions.mddocs/

Map Extensions

Enhanced map operations for both immutable and mutable maps, providing additional utility methods and conditional update operations.

General Map Extensions

MapExtensionMethods

implicit class MapExtensionMethods[K, V](private val self: Map[K, V]) extends AnyVal {
  def foreachEntry[U](f: (K, V) => U): Unit
}

foreachEntry

def foreachEntry[U](f: (K, V) => U): Unit

Iterate 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") }

Immutable Map Extensions

ImmutableMapExtensionMethods

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]
}

updatedWith

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:

  • If the function returns Some(value), the key is updated with the new value
  • If the function returns None, the key is removed from the map
  • The function receives Some(currentValue) if the key exists, None otherwise

Usage:

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)

Mutable Map Extensions

MutableMapExtensionMethods

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]
}

updateWith

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)

Map View Extensions

MapViewExtensionMethods

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.

mapValues

def mapValues[W, That](f: V => W): That

Transform 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)

filterKeys

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)

Complete Usage Examples

Cache Implementation with updatedWith

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)))
  }
}

Statistics Collection with updateWith

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)

Configuration Updates

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
}

Bulk Map Operations

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")

Map View Processing

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)

Implementation Notes

  • updatedWith creates a new immutable map, preserving the original
  • updateWith modifies the mutable map in-place and returns the result value
  • Map view operations are lazy and only computed when the view is forced
  • These methods provide functional approaches to conditional map updates
  • The remapping functions follow the same pattern as Java 8's Map.compute methods

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