0
# Collection Extensions
1
2
Enhanced collection operations including safe min/max methods, advanced transformations, and functional programming utilities.
3
4
## Safe Min/Max Operations
5
6
### TraversableOnceExtensionMethods
7
8
```scala { .api }
9
implicit class TraversableOnceExtensionMethods[A](private val self: TraversableOnce[A])
10
extends AnyVal {
11
def minOption[B >: A](implicit ord: Ordering[B]): Option[A]
12
def maxOption[B >: A](implicit ord: Ordering[B]): Option[A]
13
def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
14
def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
15
}
16
```
17
18
Safe versions of min/max operations that return `Option` instead of throwing exceptions on empty collections.
19
20
#### minOption / maxOption
21
22
```scala { .api }
23
def minOption[B >: A](implicit ord: Ordering[B]): Option[A]
24
def maxOption[B >: A](implicit ord: Ordering[B]): Option[A]
25
```
26
27
Find the minimum or maximum element in a collection.
28
29
**Returns:**
30
- `Some(element)` if the collection is non-empty
31
- `None` if the collection is empty
32
33
**Usage:**
34
```scala
35
List(3, 1, 4, 1, 5).minOption // Some(1)
36
List(3, 1, 4, 1, 5).maxOption // Some(5)
37
List.empty[Int].minOption // None
38
List.empty[Int].maxOption // None
39
```
40
41
#### minByOption / maxByOption
42
43
```scala { .api }
44
def minByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
45
def maxByOption[B](f: A => B)(implicit cmp: Ordering[B]): Option[A]
46
```
47
48
Find the element that produces the minimum or maximum value when the function is applied.
49
50
**Usage:**
51
```scala
52
case class Person(name: String, age: Int)
53
val people = List(Person("Alice", 25), Person("Bob", 30), Person("Charlie", 20))
54
55
people.minByOption(_.age) // Some(Person("Charlie", 20))
56
people.maxByOption(_.age) // Some(Person("Bob", 30))
57
List.empty[Person].minByOption(_.age) // None
58
```
59
60
## Advanced Collection Transformations
61
62
### TraversableLikeExtensionMethods
63
64
```scala { .api }
65
implicit class TraversableLikeExtensionMethods[A, Repr](
66
private val self: GenTraversableLike[A, Repr]) extends AnyVal {
67
def tapEach[U](f: A => U)(implicit bf: CanBuildFrom[Repr, A, Repr]): Repr
68
def partitionMap[A1, A2, That, Repr1, Repr2](f: A => Either[A1, A2]): (Repr1, Repr2)
69
def groupMap[K, B, That](key: A => K)(f: A => B): Map[K, That]
70
def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): Map[K, B]
71
def distinctBy[B, That](f: A => B): That
72
}
73
```
74
75
#### tapEach
76
77
```scala { .api }
78
def tapEach[U](f: A => U)(implicit bf: CanBuildFrom[Repr, A, Repr]): Repr
79
```
80
81
Apply a function to each element for its side effects and return the original collection unchanged.
82
83
**Usage:**
84
```scala
85
List(1, 2, 3)
86
.tapEach(println) // prints 1, 2, 3
87
.map(_ * 2) // List(2, 4, 6)
88
```
89
90
#### partitionMap
91
92
```scala { .api }
93
def partitionMap[A1, A2, That, Repr1, Repr2](f: A => Either[A1, A2]): (Repr1, Repr2)
94
```
95
96
Partition a collection into two collections based on a function that returns `Either`.
97
98
**Usage:**
99
```scala
100
val numbers = List(1, 2, 3, 4, 5, 6)
101
val (evens, odds) = numbers.partitionMap { n =>
102
if (n % 2 == 0) Left(n) else Right(n)
103
}
104
// evens: List(2, 4, 6)
105
// odds: List(1, 3, 5)
106
```
107
108
#### groupMap
109
110
```scala { .api }
111
def groupMap[K, B, That](key: A => K)(f: A => B): Map[K, That]
112
```
113
114
Group elements by a key function and transform them with a mapping function in one operation.
115
116
**Usage:**
117
```scala
118
case class Student(name: String, grade: Char, score: Int)
119
val students = List(
120
Student("Alice", 'A', 95),
121
Student("Bob", 'B', 85),
122
Student("Charlie", 'A', 92)
123
)
124
125
val scoresByGrade = students.groupMap(_.grade)(_.score)
126
// Map('A' -> List(95, 92), 'B' -> List(85))
127
```
128
129
#### groupMapReduce
130
131
```scala { .api }
132
def groupMapReduce[K, B](key: A => K)(f: A => B)(reduce: (B, B) => B): Map[K, B]
133
```
134
135
Group elements by a key, transform them, and reduce the values in each group.
136
137
**Usage:**
138
```scala
139
val students = List(
140
Student("Alice", 'A', 95),
141
Student("Bob", 'B', 85),
142
Student("Charlie", 'A', 92)
143
)
144
145
val avgScoreByGrade = students.groupMapReduce(_.grade)(_.score)(_ + _)
146
// Map('A' -> 187, 'B' -> 85) // Note: sum, not average
147
```
148
149
#### distinctBy
150
151
```scala { .api }
152
def distinctBy[B, That](f: A => B): That
153
```
154
155
Remove duplicate elements based on the result of a key function.
156
157
**Usage:**
158
```scala
159
case class Person(name: String, age: Int)
160
val people = List(
161
Person("Alice", 25),
162
Person("Bob", 30),
163
Person("Alice", 26) // duplicate name
164
)
165
166
val uniqueByName = people.distinctBy(_.name)
167
// List(Person("Alice", 25), Person("Bob", 30))
168
```
169
170
## Size Comparison Operations
171
172
### SizeCompareOps
173
174
```scala { .api }
175
class SizeCompareOps(private val it: Traversable[_]) extends AnyVal {
176
def <(size: Int): Boolean
177
def <=(size: Int): Boolean
178
def ==(size: Int): Boolean
179
def !=(size: Int): Boolean
180
def >=(size: Int): Boolean
181
def >(size: Int): Boolean
182
}
183
```
184
185
Efficient size comparison operations that can short-circuit for better performance with large collections.
186
187
**Usage:**
188
```scala
189
val list = List(1, 2, 3, 4, 5)
190
191
// Size comparisons
192
list.sizeIs < 10 // true
193
list.sizeIs >= 5 // true
194
list.sizeIs == 5 // true
195
list.sizeIs != 3 // true
196
197
// Sequence length comparisons
198
val seq = Seq(1, 2, 3)
199
seq.lengthIs > 2 // true
200
seq.lengthIs <= 5 // true
201
```
202
203
## Collection Utility Extensions
204
205
### TraversableExtensionMethods
206
207
```scala { .api }
208
implicit class TraversableExtensionMethods[A](private val self: Traversable[A])
209
extends AnyVal {
210
def iterableFactory: GenericCompanion[Traversable]
211
def sizeCompare(otherSize: Int): Int
212
def sizeIs: SizeCompareOps
213
def sizeCompare(that: Traversable[_]): Int
214
}
215
```
216
217
#### sizeCompare
218
219
```scala { .api }
220
def sizeCompare(otherSize: Int): Int
221
def sizeCompare(that: Traversable[_]): Int
222
```
223
224
Compare the size of a collection with an integer or another collection efficiently.
225
226
**Returns:**
227
- Negative value if collection is smaller
228
- Zero if sizes are equal
229
- Positive value if collection is larger
230
231
**Usage:**
232
```scala
233
val list1 = List(1, 2, 3)
234
val list2 = List(4, 5)
235
236
list1.sizeCompare(5) // negative (3 < 5)
237
list1.sizeCompare(3) // zero (3 == 3)
238
list1.sizeCompare(list2) // positive (3 > 2)
239
```
240
241
### SeqExtensionMethods
242
243
```scala { .api }
244
implicit class SeqExtensionMethods[A](private val self: Seq[A]) extends AnyVal {
245
def lengthIs: SizeCompareOps
246
}
247
```
248
249
Size comparison operations specifically for sequences.
250
251
## Complete Usage Examples
252
253
### Processing Data with Safe Operations
254
255
```scala
256
import scala.collection.compat._
257
258
case class Sale(product: String, amount: Double, region: String)
259
val sales = List(
260
Sale("laptop", 999.99, "north"),
261
Sale("mouse", 29.99, "south"),
262
Sale("laptop", 899.99, "north"),
263
Sale("keyboard", 79.99, "south")
264
)
265
266
// Safe aggregations
267
val maxSale = sales.maxByOption(_.amount) // Some(Sale("laptop", 999.99, "north"))
268
val minSale = sales.minByOption(_.amount) // Some(Sale("mouse", 29.99, "south"))
269
270
// Group and aggregate
271
val totalByRegion = sales.groupMapReduce(_.region)(_.amount)(_ + _)
272
// Map("north" -> 1899.98, "south" -> 109.98)
273
274
// Unique products by region
275
val productsByRegion = sales.groupMap(_.region)(_.product)
276
.view.mapValues(_.distinctBy(identity)).toMap
277
278
// Process with side effects
279
val processedSales = sales
280
.tapEach(sale => println(s"Processing ${sale.product}"))
281
.filter(_.amount > 50)
282
```
283
284
### Functional Processing Pipeline
285
286
```scala
287
def processNumbers(numbers: List[Int]): Map[String, List[Int]] = {
288
numbers
289
.tapEach(n => if (n < 0) println(s"Warning: negative number $n"))
290
.partitionMap { n =>
291
if (n % 2 == 0) Left(("even", n)) else Right(("odd", n))
292
} match {
293
case (evens, odds) =>
294
Map("even" -> evens.map(_._2), "odd" -> odds.map(_._2))
295
}
296
}
297
```