Search code examples
android-roomandroid-jetpack-compose

Compose list with section headers using group by query with paging


Given a table with many rows, I would like to query using paging if possible. When paging I would also like to group the data based on a database column and use that column as the section header for each group.

Data Table:

Id, section, data1, data2
0, "One", 1, 2
1, "One", 2, 3
2, "Two", 40, 20
3, "Two", 11, 21
4, "Three", 110, 23

Room Query:

@Query("SELECT section, * FROM data GROUP BY section ORDER BY section ASC")
fun getData(): PagingSource<Int, DataGroup>

data class DataGroup(
   @ColumnInfo(name = "section") val section: String,
   @Embedded val data: Data
)

Then in compose:

@Composable
private fun Data(
  viewModel: DataViewModel = hiltViewModel()
) {
viewModel.lights,
  val lazyItems: LazyPagingItems<DataGroup> = viewModel.data.collectAsLazyPagingItems()

  LazyColumn() {
    items(lazyItems) { item: DataGroup ->
       // I can iterate each item by this is not grouped
    }
  }
}

EDIT: Thinking about this more, I don't think a SQL "group by" is what I am after. Probably something more like:

@Query("SELECT section, * FROM data ORDER BY section ASC")
fun getData(): List<Data>

With:

val groupedData = dao.getData.groupBy { it.section }

However at that point I lose the advantage of paging. Possible to achieve this grouped by section result with paging?


Solution

  • It seems the paging library already has a utility to handle this. In my case:

    @Query("SELECT section, * FROM data ORDER BY section ASC")
    fun getData(): PagingSource<Int, Data>
    

    Then map the data, and use insertSeparators to determine where to place the section headers:

    val data: Flow<PagingData<Data>> = Pager(PagingConfig(pageSize = 20), null) {
       repository.getData()
    }.flow
       .map { pagingData ->
          mapped.insertSeparators { data1: Data?, data2: Data? ->
             if (data1?.section != data2?.section) {
               Data(section = data2?.section, isHeader = true)
             } else null
          }
    }