Categories

# Scala Saturday – The Stream.grouped Method

Another method `Stream` offers is `Stream.grouped`, which divides a stream’s elements into groups of a given size.

To take an example, if you have a stream of twelve elements and call `Stream.grouped` to turn it into groups of three, you’ll get an iterator over four sequences, each three elements in size:

```val xs = (1 to 12).toStream
val grouped = xs.grouped(3)
// grouped: Iterator[Stream[Int]] =
//   Iterator(
//     Stream(1, 2, 3), Stream(4, 5, 6),
//     Stream(7, 8, 9), Stream(10, 11, 12))
```

What happens if you use a group size that does not divide evenly into the size of your input stream? No sweat! The last group just contains any remaining elements, however many they may be:

```val xs = (1 to 10).toStream
val grouped = xs.grouped(3)
// grouped: Iterator[Stream[Int]] =
//   Iterator(
//     Stream(1, 2, 3), Stream(4, 5, 6),
//     Stream(7, 8, 9), Stream(10))
```

Where is this useful? Well, you can take my paging example from my Scala Saturday post on `Stream.drop` and make it slightly clearer without the `(page - 1) * perPage` arithmetic:

```case class Book(title: String, author: String)

val books = Stream(
Book("Wuthering Heights", "Emily Bronte"),
Book("Jane Eyre", "Charlotte Bronte"),
Book("Agnes Grey", "Anne Bronte"),
Book("The Scarlet Letter", "Nathaniel Hawthorne"),
Book("Silas Marner", "George Eliot"),
Book("1984", "George Orwell"),
Book("Billy Budd", "Herman Melville"),
Book("Moby Dick", "Herman Melville"),
Book("The Great Gatsby", "F. Scott Fitzgerald"),
Book("Tom Sawyer", "Mark Twain")
)

val perPage = 3
val page = 3
val records = books.grouped(perPage)
.drop(page - 1)
.next
// records: scala.collection.immutable.Stream[Book] =
//   Stream(Book(Billy Budd,Herman Melville),
//     Book(Moby Dick,Herman Melville),
//     Book(The Great Gatsby,F. Scott Fitzgerald))
```

This time, instead of having to calculate the number of elements to skip in order to skip n pages, you first use `Stream.grouped` to turn the stream into a paged recordset; each “page” is n records long. Then drop `page - 1` pages in order to get to the page of records you want. Finally, calling `Iterator.next` is necessary because, remember, `Stream.grouped` turns a flat stream into a stream of streams.

I will admit that I find it irritating that `Stream.grouped` returns something that does not have a `head` method. Calling `Iterator.next`, while just as easy, is inconsistent with collection semantics. It seems to me that `Stream.grouped` ought to return a collection rather than an iterator. Perhaps there was once a reason for returning an iterator instead of a collection, but it would be nice if we could fix that.

This site uses Akismet to reduce spam. Learn how your comment data is processed.