or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotation-backports.mdbackported-collections.mdcollection-extensions.mdcollection-factories.mdindex.mditerator-size-ops.mdjava-interop.mdmap-extensions.mdmethod-chaining.mdoption-converters.mdresource-management.mdstring-parsing.md

iterator-size-ops.mddocs/

0

# Iterator and Size Operations

1

2

Enhanced iterator methods and efficient size comparison utilities for collections.

3

4

## Iterator Extensions

5

6

### IteratorExtensionMethods

7

8

```scala { .api }

9

implicit class IteratorExtensionMethods[A](private val self: Iterator[A]) extends AnyVal {

10

def nextOption(): Option[A]

11

def sameElements[B >: A](that: TraversableOnce[B]): Boolean

12

def concat[B >: A](that: TraversableOnce[B]): TraversableOnce[B]

13

def tapEach[U](f: A => U): Iterator[A]

14

}

15

```

16

17

#### nextOption

18

19

```scala { .api }

20

def nextOption(): Option[A]

21

```

22

23

Safe version of `next()` that returns `None` when the iterator is exhausted instead of throwing an exception.

24

25

**Usage:**

26

```scala

27

val iterator = List(1, 2, 3).iterator

28

29

iterator.nextOption() // Some(1)

30

iterator.nextOption() // Some(2)

31

iterator.nextOption() // Some(3)

32

iterator.nextOption() // None (instead of NoSuchElementException)

33

```

34

35

#### sameElements

36

37

```scala { .api }

38

def sameElements[B >: A](that: TraversableOnce[B]): Boolean

39

```

40

41

Compare two iterators/traversables element by element for equality.

42

43

**Usage:**

44

```scala

45

val iter1 = List(1, 2, 3).iterator

46

val iter2 = Vector(1, 2, 3).iterator

47

val iter3 = List(1, 2, 4).iterator

48

49

iter1.sameElements(iter2) // true

50

iter1.sameElements(iter3) // false

51

52

// Works with any TraversableOnce

53

val list = List(1, 2, 3)

54

list.iterator.sameElements(list) // true

55

```

56

57

#### concat

58

59

```scala { .api }

60

def concat[B >: A](that: TraversableOnce[B]): TraversableOnce[B]

61

```

62

63

Concatenate the iterator with another `TraversableOnce`.

64

65

**Usage:**

66

```scala

67

val iter1 = List(1, 2, 3).iterator

68

val iter2 = List(4, 5, 6)

69

70

val combined = iter1.concat(iter2)

71

combined.toList // List(1, 2, 3, 4, 5, 6)

72

```

73

74

#### tapEach

75

76

```scala { .api }

77

def tapEach[U](f: A => U): Iterator[A]

78

```

79

80

Apply a function to each element for side effects while preserving the iterator.

81

82

**Usage:**

83

```scala

84

val iterator = List(1, 2, 3, 4, 5).iterator

85

86

val processed = iterator

87

.tapEach(x => println(s"Processing: $x"))

88

.filter(_ % 2 == 0)

89

.tapEach(x => println(s"Even number: $x"))

90

91

processed.toList

92

// Output:

93

// Processing: 1

94

// Processing: 2

95

// Even number: 2

96

// Processing: 3

97

// Processing: 4

98

// Even number: 4

99

// Processing: 5

100

// Result: List(2, 4)

101

```

102

103

## Size Comparison Operations

104

105

### SizeCompareOps

106

107

```scala { .api }

108

class SizeCompareOps(private val it: Traversable[_]) extends AnyVal {

109

def <(size: Int): Boolean

110

def <=(size: Int): Boolean

111

def ==(size: Int): Boolean

112

def !=(size: Int): Boolean

113

def >=(size: Int): Boolean

114

def >(size: Int): Boolean

115

}

116

```

117

118

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

119

120

#### Size Comparison Methods

121

122

```scala { .api }

123

def <(size: Int): Boolean // Tests if collection size is less than given size

124

def <=(size: Int): Boolean // Tests if collection size is less than or equal to given size

125

def ==(size: Int): Boolean // Tests if collection size equals given size

126

def !=(size: Int): Boolean // Tests if collection size is not equal to given size

127

def >=(size: Int): Boolean // Tests if collection size is greater than or equal to given size

128

def >(size: Int): Boolean // Tests if collection size is greater than given size

129

```

130

131

**Usage:**

132

```scala

133

import scala.collection.compat._

134

135

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

136

137

// Size comparisons - more efficient than list.size comparisons

138

list.sizeIs < 10 // true

139

list.sizeIs <= 5 // true

140

list.sizeIs == 5 // true

141

list.sizeIs != 3 // true

142

list.sizeIs >= 5 // true

143

list.sizeIs > 2 // true

144

145

// Works with any Traversable

146

val set = Set("a", "b", "c")

147

set.sizeIs == 3 // true

148

set.sizeIs > 5 // false

149

```

150

151

### TraversableExtensionMethods

152

153

```scala { .api }

154

implicit class TraversableExtensionMethods[A](private val self: Traversable[A])

155

extends AnyVal {

156

def iterableFactory: GenericCompanion[Traversable]

157

def sizeCompare(otherSize: Int): Int

158

def sizeIs: SizeCompareOps

159

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

160

}

161

```

162

163

#### sizeCompare

164

165

```scala { .api }

166

def sizeCompare(otherSize: Int): Int

167

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

168

```

169

170

Compare collection size with an integer or another collection, returning negative/zero/positive like `Ordering`.

171

172

**Returns:**

173

- Negative value if the collection is smaller

174

- Zero if sizes are equal

175

- Positive value if the collection is larger

176

177

**Usage:**

178

```scala

179

val list1 = List(1, 2, 3)

180

val list2 = List(4, 5)

181

182

list1.sizeCompare(5) // negative (3 < 5)

183

list1.sizeCompare(3) // zero (3 == 3)

184

list1.sizeCompare(2) // positive (3 > 2)

185

186

list1.sizeCompare(list2) // positive (3 > 2)

187

list2.sizeCompare(list1) // negative (2 < 3)

188

```

189

190

#### sizeIs

191

192

```scala { .api }

193

def sizeIs: SizeCompareOps

194

```

195

196

Get a `SizeCompareOps` instance for the collection to perform size comparisons.

197

198

### SeqExtensionMethods

199

200

```scala { .api }

201

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

202

def lengthIs: SizeCompareOps

203

}

204

```

205

206

#### lengthIs

207

208

```scala { .api }

209

def lengthIs: SizeCompareOps

210

```

211

212

Size comparison operations specifically for sequences (using length terminology).

213

214

**Usage:**

215

```scala

216

val seq = Seq(1, 2, 3, 4)

217

218

seq.lengthIs < 10 // true

219

seq.lengthIs == 4 // true

220

seq.lengthIs > 5 // false

221

```

222

223

## Performance Benefits

224

225

### Short-Circuit Evaluation

226

227

Size comparison operations can short-circuit, providing better performance than computing the full size:

228

229

```scala

230

val largeList = (1 to 1000000).toList

231

232

// Inefficient - computes full size

233

largeList.size > 100 // Processes all 1M elements

234

235

// Efficient - stops after 101 elements

236

largeList.sizeIs > 100 // Only processes 101 elements

237

```

238

239

### Lazy Collections

240

241

Size operations work efficiently with lazy collections:

242

243

```scala

244

import scala.collection.compat.immutable._

245

246

val infiniteStream = LazyList.from(1)

247

248

// These operations terminate quickly

249

infiniteStream.sizeIs > 5 // true (checks first 6 elements)

250

infiniteStream.sizeIs == 10 // false (checks first 11 elements)

251

252

// This would never terminate

253

// infiniteStream.size // Don't do this!

254

```

255

256

## Complete Usage Examples

257

258

### Processing Large Datasets

259

260

```scala

261

import scala.collection.compat._

262

263

def processDataBatch[T](data: Iterable[T], maxBatchSize: Int): List[List[T]] = {

264

if (data.sizeIs <= maxBatchSize) {

265

List(data.toList)

266

} else {

267

data.grouped(maxBatchSize).toList

268

}

269

}

270

271

// Efficient - stops counting after maxBatchSize + 1 elements

272

val batches = processDataBatch(largeDataset, 1000)

273

```

274

275

### Validation and Constraints

276

277

```scala

278

def validateCollectionSizes[T](

279

required: Traversable[T],

280

optional: Traversable[T]

281

): Either[String, Unit] = {

282

283

if (required.sizeIs == 0) {

284

Left("Required items cannot be empty")

285

} else if (required.sizeIs > 100) {

286

Left("Too many required items (max 100)")

287

} else if (optional.sizeIs > 50) {

288

Left("Too many optional items (max 50)")

289

} else {

290

Right(())

291

}

292

}

293

```

294

295

### Iterator Processing with Debugging

296

297

```scala

298

def processUserData(users: Iterator[User]): List[ProcessedUser] = {

299

users

300

.tapEach(user => println(s"Processing user: ${user.id}"))

301

.filter(_.isActive)

302

.tapEach(user => println(s"Active user: ${user.id}"))

303

.take(100) // Limit processing

304

.tapEach(user => println(s"Within limit: ${user.id}"))

305

.map(processUser)

306

.toList

307

}

308

309

case class User(id: String, isActive: Boolean, name: String)

310

case class ProcessedUser(id: String, name: String, processedAt: Long)

311

312

def processUser(user: User): ProcessedUser =

313

ProcessedUser(user.id, user.name, System.currentTimeMillis())

314

```

315

316

### Safe Iterator Consumption

317

318

```scala

319

def safeIteratorProcessing[T](iterator: Iterator[T]): List[T] = {

320

val buffer = scala.collection.mutable.ListBuffer[T]()

321

322

while (iterator.hasNext) {

323

iterator.nextOption() match {

324

case Some(element) => buffer += element

325

case None => // Iterator was exhausted between hasNext and next

326

}

327

}

328

329

buffer.toList

330

}

331

332

// Alternative using tap for logging

333

def loggedIteratorProcessing[T](iterator: Iterator[T]): List[T] = {

334

iterator

335

.tapEach(element => println(s"Processing: $element"))

336

.toList

337

}

338

```

339

340

### Comparing Collection Sizes Efficiently

341

342

```scala

343

def selectSmallestCollection[T](collections: List[Traversable[T]]): Option[Traversable[T]] = {

344

collections match {

345

case Nil => None

346

case head :: tail =>

347

Some(tail.foldLeft(head) { (smallest, current) =>

348

if (current.sizeCompare(smallest) < 0) current else smallest

349

})

350

}

351

}

352

353

val lists = List(

354

List(1, 2, 3),

355

List(4, 5, 6, 7, 8),

356

List(9, 10)

357

)

358

359

val smallest = selectSmallestCollection(lists) // Some(List(9, 10))

360

```

361

362

### Batch Processing with Size Constraints

363

364

```scala

365

def createBalancedBatches[T](

366

items: Traversable[T],

367

minBatchSize: Int,

368

maxBatchSize: Int

369

): List[List[T]] = {

370

371

if (items.sizeIs < minBatchSize) {

372

List(items.toList) // Single batch if too few items

373

} else {

374

val iterator = items.toIterator

375

val batches = scala.collection.mutable.ListBuffer[List[T]]()

376

377

while (iterator.hasNext) {

378

val batch = iterator.take(maxBatchSize).toList

379

if (batch.sizeIs >= minBatchSize || !iterator.hasNext) {

380

batches += batch

381

} else {

382

// Combine small final batch with previous batch

383

if (batches.nonEmpty) {

384

val lastBatch = batches.remove(batches.length - 1)

385

batches += (lastBatch ++ batch)

386

} else {

387

batches += batch

388

}

389

}

390

}

391

392

batches.toList

393

}

394

}

395

```

396

397

## Implementation Notes

398

399

- Size comparison operations use optimized implementations that can short-circuit

400

- For `Seq` types, `lengthCompare` is used when available for O(1) comparisons

401

- Iterator extensions preserve lazy evaluation where possible

402

- `tapEach` creates a new iterator that applies the side effect function during iteration

403

- All operations are implemented as value classes for minimal runtime overhead