You can use the implicit class with the implicit CanBuildFrom . This uses a volatile builder, but not on the caller 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() } } }
for use as follows:
import MyResultSetContainer._ val rs = stmnt.executeQuery("select * from pg_user") val names = for (row <- rs) yield (row.getString(1)) println(names) rs.close()
For understanding, map used under the hood, so if you prefer map directly:
val names = rs.map(row => row.getString(1))
which creates a sequence. Thanks to CanBuildFrom you can create other collections by explicitly providing a type:
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 a resulting type and a type returned by a function called map. Based on this information, the Scala compiler provides the factory implicitly, which can be used to create a suitable builder. Therefore, you only need one method to create different types of collections.
If you want to return multiple values, just return the tuple:
val columns = rs.map(row => (row.getInt(2), row.getString(1)))
and the tuple can be used to directly create a map :
val keyNamesMap: Map[Int, String] = rs.map(row => (row.getInt(2), row.getString(1)))
This is based on the idea that the result set is a list of strings, so the map function should be available on top of it. An implicit class is used to add the map method to the base result set implicitly.