You could use an implicit class together with an implicit CanBuildFrom
. This does use a mutable builder, but not at the caller's side:
object MyResultSetContainer {
implicit class MyResultSet(rs: ResultSet) {
def map[T, C <: Iterable[T]](f: (ResultSet) => T)
(implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
val builder = cbf()
while (rs.next()) {
builder += f(rs)
}
builder.result()
}
}
}
to be used like this:
import MyResultSetContainer._
val rs = stmnt.executeQuery("select * from pg_user")
val names = for (row <- rs) yield (row.getString(1))
println(names)
rs.close()
The for comprehension uses map
under the hood, so if you prefer map
directly:
val names = rs.map(row => row.getString(1))
which produces a sequence. Thanks to CanBuildFrom
you can produce other collections as well by providing a type explicitly:
val names: List[String] = rs.map(row => row.getString(1))
How does CanBuildFrom
work? The Scala compiler looks at the types involved in this expression: There is the resulting type, and the type returned by the function called by map. Based on this information, the Scala compiler provides a factory implicitly which can be used to create a suitable builder. So you need only one method to produce different types of collections.
If you want to return multiple values, just return a tuple:
val columns = rs.map(row => (row.getInt(2), row.getString(1)))
and the tuple can be used to create a Map
directly:
val keyNamesMap: Map[Int, String] =
rs.map(row => (row.getInt(2), row.getString(1)))
This is based on the idea that a result set is a list of rows, and so the map
function should be available on top of it. The implicit class is used to add the map
method to the underlying result set implicitly.