0
# Map Extensions
1
2
Enhanced map operations for both immutable and mutable maps, providing additional utility methods and conditional update operations.
3
4
## General Map Extensions
5
6
### MapExtensionMethods
7
8
```scala { .api }
9
implicit class MapExtensionMethods[K, V](private val self: Map[K, V]) extends AnyVal {
10
def foreachEntry[U](f: (K, V) => U): Unit
11
}
12
```
13
14
#### foreachEntry
15
16
```scala { .api }
17
def foreachEntry[U](f: (K, V) => U): Unit
18
```
19
20
Iterate over key-value pairs with a function that takes separate key and value parameters instead of a tuple.
21
22
**Usage:**
23
```scala
24
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
25
26
// Traditional foreach with tuple
27
map.foreach { case (k, v) => println(s"$k: $v") }
28
29
// foreachEntry with separate parameters
30
map.foreachEntry { (k, v) => println(s"$k: $v") }
31
```
32
33
## Immutable Map Extensions
34
35
### ImmutableMapExtensionMethods
36
37
```scala { .api }
38
implicit class ImmutableMapExtensionMethods[K, V](
39
private val self: immutable.Map[K, V]) extends AnyVal {
40
def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): Map[K, V1]
41
}
42
```
43
44
#### updatedWith
45
46
```scala { .api }
47
def updatedWith[V1 >: V](key: K)(remappingFunction: Option[V] => Option[V1]): Map[K, V1]
48
```
49
50
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`.
51
52
**Behavior:**
53
- If the function returns `Some(value)`, the key is updated with the new value
54
- If the function returns `None`, the key is removed from the map
55
- The function receives `Some(currentValue)` if the key exists, `None` otherwise
56
57
**Usage:**
58
```scala
59
val map = Map("a" -> 1, "b" -> 2)
60
61
// Increment existing value or set to 1 if not present
62
val incremented = map.updatedWith("a") {
63
case Some(value) => Some(value + 1)
64
case None => Some(1)
65
}
66
// Result: Map("a" -> 2, "b" -> 2)
67
68
// Remove key conditionally
69
val filtered = map.updatedWith("a") {
70
case Some(value) if value > 1 => None // Remove if > 1
71
case other => other // Keep as is
72
}
73
// Result: Map("b" -> 2)
74
75
// Add new key
76
val withNew = map.updatedWith("c") {
77
case Some(value) => Some(value + 10)
78
case None => Some(100)
79
}
80
// Result: Map("a" -> 1, "b" -> 2, "c" -> 100)
81
```
82
83
## Mutable Map Extensions
84
85
### MutableMapExtensionMethods
86
87
```scala { .api }
88
implicit class MutableMapExtensionMethods[K, V](
89
private val self: mutable.Map[K, V]) extends AnyVal {
90
def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V]
91
}
92
```
93
94
#### updateWith
95
96
```scala { .api }
97
def updateWith(key: K)(remappingFunction: Option[V] => Option[V]): Option[V]
98
```
99
100
Conditionally update a mutable map entry in-place. Similar to `updatedWith` but modifies the map directly and returns the final value.
101
102
**Returns:** The final value associated with the key after the update, or `None` if the key was removed.
103
104
**Usage:**
105
```scala
106
val map = mutable.Map("a" -> 1, "b" -> 2)
107
108
// Increment existing value or set to 1 if not present
109
val result1 = map.updateWith("a") {
110
case Some(value) => Some(value + 1)
111
case None => Some(1)
112
}
113
// map is now Map("a" -> 2, "b" -> 2)
114
// result1 is Some(2)
115
116
// Remove key conditionally
117
val result2 = map.updateWith("a") {
118
case Some(value) if value > 1 => None
119
case other => other
120
}
121
// map is now Map("b" -> 2)
122
// result2 is None
123
124
// Add new key
125
val result3 = map.updateWith("c") {
126
case Some(value) => Some(value + 10)
127
case None => Some(100)
128
}
129
// map is now Map("b" -> 2, "c" -> 100)
130
// result3 is Some(100)
131
```
132
133
## Map View Extensions
134
135
### MapViewExtensionMethods
136
137
```scala { .api }
138
implicit class MapViewExtensionMethods[K, V, C <: Map[K, V]](
139
private val self: IterableView[(K, V), C]) extends AnyVal {
140
def mapValues[W, That](f: V => W): That
141
def filterKeys(p: K => Boolean): IterableView[(K, V), C]
142
}
143
```
144
145
Extensions for map views that provide lazy transformation operations.
146
147
#### mapValues
148
149
```scala { .api }
150
def mapValues[W, That](f: V => W): That
151
```
152
153
Transform the values in a map view lazily.
154
155
**Usage:**
156
```scala
157
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
158
val view = map.view
159
160
val doubled = view.mapValues(_ * 2)
161
// Lazy view - values not computed until accessed
162
doubled.force // Map("a" -> 2, "b" -> 4, "c" -> 6)
163
```
164
165
#### filterKeys
166
167
```scala { .api }
168
def filterKeys(p: K => Boolean): IterableView[(K, V), C]
169
```
170
171
Filter map entries by keys in a lazy view.
172
173
**Usage:**
174
```scala
175
val map = Map("apple" -> 1, "banana" -> 2, "apricot" -> 3)
176
val view = map.view
177
178
val aWords = view.filterKeys(_.startsWith("a"))
179
// Lazy view - filtering not applied until accessed
180
aWords.force // Map("apple" -> 1, "apricot" -> 3)
181
```
182
183
## Complete Usage Examples
184
185
### Cache Implementation with updatedWith
186
187
```scala
188
import scala.collection.compat._
189
190
case class CacheEntry[T](value: T, timestamp: Long)
191
192
class Cache[K, V] {
193
private var store = Map.empty[K, CacheEntry[V]]
194
private val ttlMs = 60000 // 1 minute TTL
195
196
def get(key: K): Option[V] = {
197
val now = System.currentTimeMillis()
198
store.get(key) match {
199
case Some(entry) if now - entry.timestamp < ttlMs =>
200
Some(entry.value)
201
case _ =>
202
store = store.updatedWith(key)(_ => None) // Remove expired entry
203
None
204
}
205
}
206
207
def put(key: K, value: V): Unit = {
208
val now = System.currentTimeMillis()
209
store = store.updatedWith(key)(_ => Some(CacheEntry(value, now)))
210
}
211
}
212
```
213
214
### Statistics Collection with updateWith
215
216
```scala
217
import scala.collection.{mutable, compat}
218
import compat._
219
220
val stats = mutable.Map.empty[String, Int]
221
222
def recordEvent(event: String): Unit = {
223
stats.updateWith(event) {
224
case Some(count) => Some(count + 1)
225
case None => Some(1)
226
}
227
}
228
229
recordEvent("login") // stats: Map("login" -> 1)
230
recordEvent("logout") // stats: Map("login" -> 1, "logout" -> 1)
231
recordEvent("login") // stats: Map("login" -> 2, "logout" -> 1)
232
```
233
234
### Configuration Updates
235
236
```scala
237
case class Config(timeout: Int, retries: Int, enabled: Boolean)
238
239
val baseConfig = Map(
240
"api" -> Config(5000, 3, true),
241
"db" -> Config(30000, 1, true)
242
)
243
244
// Disable a service conditionally
245
val updatedConfig = baseConfig.updatedWith("api") {
246
case Some(config) if config.timeout > 10000 => Some(config.copy(enabled = false))
247
case other => other
248
}
249
250
// Add new service configuration
251
val withNewService = updatedConfig.updatedWith("cache") {
252
case Some(existing) => Some(existing) // Keep existing
253
case None => Some(Config(1000, 5, true)) // Add new
254
}
255
```
256
257
### Bulk Map Operations
258
259
```scala
260
def processUserPreferences(
261
current: Map[String, String],
262
updates: List[(String, Option[String])]
263
): Map[String, String] = {
264
updates.foldLeft(current) { case (map, (key, valueOpt)) =>
265
map.updatedWith(key)(_ => valueOpt)
266
}
267
}
268
269
val prefs = Map("theme" -> "dark", "lang" -> "en")
270
val updates = List(
271
"theme" -> Some("light"), // Update existing
272
"lang" -> None, // Remove existing
273
"region" -> Some("us") // Add new
274
)
275
276
val result = processUserPreferences(prefs, updates)
277
// Result: Map("theme" -> "light", "region" -> "us")
278
```
279
280
### Map View Processing
281
282
```scala
283
val inventory = Map(
284
"laptop" -> 999.99,
285
"mouse" -> 29.99,
286
"keyboard" -> 79.99,
287
"monitor" -> 299.99
288
)
289
290
// Chain view operations for efficient processing
291
val expensiveItems = inventory.view
292
.filterKeys(!_.contains("mouse")) // Exclude mice
293
.mapValues(_ * 1.1) // Add 10% markup
294
.filter(_._2 > 100) // Only expensive items
295
.toMap
296
297
// Result: Map("laptop" -> 1099.989, "monitor" -> 329.989)
298
```
299
300
## Implementation Notes
301
302
- `updatedWith` creates a new immutable map, preserving the original
303
- `updateWith` modifies the mutable map in-place and returns the result value
304
- Map view operations are lazy and only computed when the view is forced
305
- These methods provide functional approaches to conditional map updates
306
- The remapping functions follow the same pattern as Java 8's `Map.compute` methods