I'm trying to build a `List<List<Pacs>&gt...
# announcements
r
I'm trying to build a
List<List<Pacs>>
from a large
List<Pacs>
. I created a function
buildGroupedList
that takes
List<Pacs>
as argument.
Copy code
data class Pacs(
    val priorityCode: String,
    val groupCode: String? = null
)
Copy code
val lst = listOf(
    Pacs("RV"),
    Pacs("RF", "GRP1"),
    Pacs("RID", "GRP1"),
    Pacs("RI", "GRP1"),
    Pacs("RID3", "GRP2"),
    Pacs("RID4", "GRP2"),
    Pacs("RF1"),
    Pacs("RV4"),
    Pacs("RI5", "GRP1"),
    Pacs("RIT", "GRP2")
)
Copy code
fun buildGroupedList(pacsList: List<Pacs>): List<List<Pacs>> {
    val result = mutableListOf<List<Pacs>>()
    var lst = mutableListOf<Pacs>()
    var previousGroupCode: String? = null

    for (pacs in pacsList) {
        if (pacs.groupCode == null) {
            if (previousGroupCode.isNullOrBlank())
                continue
            else {
                previousGroupCode = null
                lst = mutableListOf()
            }
        } else {
            if (previousGroupCode.isNullOrBlank()) {
                previousGroupCode = pacs.groupCode
                lst.add(pacs)
            } else {
                if (pacs.groupCode == previousGroupCode) {
                    lst.add(pacs)
                } else {
                    result.add(lst)
                    previousGroupCode = pacs.groupCode
                    lst = mutableListOf()
                    lst.add(pacs)
                }
            }
        }
    }

    return result
}
currently, its printing
[[Pacs(priorityCode=RF, groupCode=GRP1), Pacs(priorityCode=RID, groupCode=GRP1), Pacs(priorityCode=RI, groupCode=GRP1)], [Pacs(priorityCode=RI5, groupCode=GRP1)]]
but expected result should be:
[[Pacs(priorityCode=RF, groupCode=GRP1), Pacs(priorityCode=RID, groupCode=GRP1), Pacs(priorityCode=RI, groupCode=GRP1)], [Pacs(priorityCode=RID3, groupCode=GRP2), Pacs(priorityCode=RID4, groupCode=GRP2)], [Pacs(priorityCode=RI5, groupCode=GRP1)], [Pacs(priorityCode=RIT, groupCode=GRP2)]]
it must not include
Pacs
with null
groupCode
and only contiguous
Pacs
with same
groupCode
must be in a same list.
order is important.
not sure why GRP2 is not being included in my function implementation?
any help is very much appreciated. thanks.
r
I can see 2 problems: a) when you encounter
null
group Pacs, then you just forget your built up group (
pacs.groupCode == null && previousCode != null
branch) - imho this can be easily fixed with just ignoring all null groups b) at the end of the list, you forget your built up group if you see, GRP2 in your list fits in either of these 2 branches, so you won't get it in output
ah, well question is what should happen in case like this:
[1, null, 1]
2 choices:
[[1], [1]]
or
[[1, 1]]
r
[[1], [1]]
 this would be expectation
I modified based on what I understood from your comments:
fun buildGroupedList(pacsList: List<Pacs>): List<List<Pacs>> {
val result = mutableListOf<List<Pacs>>()
var lst = mutableListOf<Pacs>()
var previousGroupCode: String? = null
for (pacs in pacsList) {
if (pacs.groupCode != null) {
if (previousGroupCode.isNullOrBlank()) {
previousGroupCode = pacs.groupCode
lst.add(pacs)
} else {
if (pacs.groupCode == previousGroupCode) {
lst.add(pacs)
} else {
result.add(lst)
previousGroupCode = pacs.groupCode
lst = mutableListOf()
lst.add(pacs)
}
}
}
}
return result
}
and the result is:
Copy code
[[Pacs(priorityCode=RF, groupCode=GRP1), Pacs(priorityCode=RID, groupCode=GRP1), Pacs(priorityCode=RI, groupCode=GRP1)], [Pacs(priorityCode=RID3, groupCode=GRP2), Pacs(priorityCode=RID4, groupCode=GRP2)], [Pacs(priorityCode=RI5, groupCode=GRP1)]]
last GRP2 (in its own list) is missing from the final result list
r
ye, you have to have "if group is not empty, add it" after the for-cycle otherwise it won't be there ever
r
apologies, I do not understand that logic, why do I need special logic for the last element?
if i use the following test data:
Copy code
val lst = listOf(
    Pacs("RV"),
    Pacs("RF", "GRP1"),
    Pacs("RID", "GRP1"),
    Pacs("RI", "GRP1"),
    Pacs("RID3", "GRP2"),
    Pacs("RID4", "GRP2"),
    Pacs("RF1"),
    Pacs("RV4"),
    Pacs("RI5", "GRP1"),
    Pacs("RIT1", "GRP2"),
    Pacs("RIT2", "GRP2"),
    Pacs("RIT3", "GRP2"),
    Pacs("RIT4", "GRP2"),
    Pacs("RIT"),
)
it'll still print
[[Pacs(priorityCode=RF, groupCode=GRP1), Pacs(priorityCode=RID, groupCode=GRP1), Pacs(priorityCode=RI, groupCode=GRP1)], [Pacs(priorityCode=RID3, groupCode=GRP2), Pacs(priorityCode=RID4, groupCode=GRP2)], [Pacs(priorityCode=RI5, groupCode=GRP1)]]
r
because you only add to the result when the group changes - at the end of list the group does not change, and you still may have something in the
lst
variable and I think I mentioned, if you want the
[[1], [1]]
result in the
[1, null, 1]
case, then you need to treat
null
groups as proper full groups too - can't just ignore as I suggested at first, because then it would output
[[1, 1]]
Here is my most readable implementation so far:
Copy code
fun buildGroupedListSeq(pacsList: List<Pacs>): List<List<Pacs>> = sequence<List<Pacs>> {
    var currentCode: String? = null             // Let's start with empty <null> group
    var group = mutableListOf<Pacs>()

    for (pacs in pacsList) {
        if (currentCode != pacs.groupCode) {    // if group changes
            yield(group)                        // produce list to output
            group = mutableListOf()             // set up new group
            currentCode = pacs.groupCode
        }

        group.add(pacs)                         // group rn will be either empty, and so we'll add new this one
                                                // or will be old group so we add there
    }

    yield(group)                                // also need to yield last group
}
    .filterNot { it.isEmpty() }                 // above might have produced empty groups - at start and at end, so filter em out
    .filter { it[0].groupCode != null }         // filter out null groups 
    .toList()
it's using
sequence
tho, so might not be best idea if you need it extra super duper performant. Otherwise this is probs most readable you can get it
r
thanks Roukanken for the code sample. this seems to be grouping items in their own list as expected. I'm gonna create some more test data based on the actual business needs to make sure Pacs are grouped correctly at all times. as of now, I'm not super worried about performance though.
r
The performance only starts being visible when you have huuuge data, or call it very very quickly Otherwise as always: prefer as readable as possible, optimalize only when needed
💯 1
n
why can't you use the stdlib function chunked for this?
r
@nkiesel because there's no definite way to say that how many of Pacs will in one small list. the big list of many smaller lists have all smaller lists of varying size and dynamic. the example I used is just one sample.
@nkiesel I would love to see the code to solve this problem using
chunked
though.
n
sorry, was too quick reading the question. How about https://pl.kotl.in/2L8V_p5-u
slightly longer but more optimized (avoids collecting pacs with
null
code): https://pl.kotl.in/d3bo_Oxib
r
@nkiesel unfortunately, both solutions are missing the last entry of
[Pacs(priorityCode=RIT, groupCode=GRP2)]
at the end.
n
https://pl.kotl.in/2L8V_p5-u (sorry about that)
r
@nkiesel same issue, still missing the last Pacs with GRP2
n
please try to refresh. the code now should have a
if (currentCode != null) groupedList.add(currentList)
line just before the return
hmm, got a new link now. https://pl.kotl.in/z0_ql-PCl It seems play.kotlinlang.org plays wierd tricks with cookies. try opening in "private tab"
r
yes, now I see the updated code that you mentioned earlier. it seems to have all correct groups. it weird that chrome browser does this funky caching thing 🙂.
n
nothing to do with chrome browser. same happens for me with firefox. the problem is that play.kotlinlang.org uses "clever" code to always load your latest code. This is nice as long as you don't want to share it and/or have multiple tabs with different code snippets. Thus the recommendation to use "private tab": that blocks existing cookies and you thus get a fresh page (or the shared page if you use a shared link)
💯 1
I just scrolled back through the thread and saw the "sequence" approach. depending on your actual input size that might actually be better because it does not require all the sublists to be kept in memory. I still think the
if (currentCode != null)
inline conditions are nicer than filtering at the end because that defeats the whole "sequence" advantage. See https://pl.kotl.in/Q1GXBsuLQ for the resulting code. The function no longer has the
groupedList
variable and when I print the results one sublist at a time, I never have to collapse the sequence in a complete list of sublists.
r
appreciate the improvement. definitely looks better than previous non-sequence approach.
n
You might have to use
var currentCode = pacsList.firstOrNull()?.groupCode
if the input list can be empty
else you get a
NoSuchElementException
👍 1
r
yes. it's good to follow safe approach to avoid nasty exceptions.
thanks @Roukanken and @nkiesel for taking out from your valuable time and replying with solutions. appreciate it. 🙏