Kotlin on Android: map a cursor to a list
Cursor
s are meant to be lazy so there is no obvious map
. However Anko (ANdroid KOtlin by JetBrains) does have an extension method to help you with mapping Cursor
.
The method is called Cursor.parseList(parser)
. Here you can find full implementation of it. Usage should be straight-forward. And here are some official docs on sqlite and parsing cursors with anko.
You can write your own map()
which checks against isClosed()
and safely close the Cursor
after iterating all over its rows like below:
fun <T> Cursor.map(f: (Cursor) -> T): List<T> {
val items = mutableListOf<T>()
use {
while (!it.isClosed && it.moveToNext()) {
items += f(it)
}
}
return items.toList()
}
Sample usage:
val contacts = cursor.map { it.toContact() }
You can use this extension:
fun <T> Cursor.toArrayList(block: (Cursor) -> T) : ArrayList<T> {
return arrayListOf<T>().also { list ->
if (moveToFirst()) {
do {
list.add(block.invoke(this))
} while (moveToNext())
}
}
}
and use it:
val list = cursor.toArrayList { getStringFromCursor(it) }
This is what I went with in the end, using kotlin.sequences.generateSequence
...
val list = generateSequence { if (c.moveToNext()) c else null }
.map { getStringFromCursor(it) }
.toList()
My first attempt was a little shorter:
val list = (1 .. c.count).map {
c.moveToNext()
getStringFromCursor(c)
}
Both versions rely on the cursor initially being positioned before the first record (as a new cursor is). The second would throw if that wasn't the case, while the first would return a shorter list.