How to split array of objects into subarray based on multiple property/field values?

60 views Asked by At

I have a list of objects. Here are the object properties (they are a hierarchy) -

School District Id/Name

School Id/Name

Grade Id/Name

Subject Id/Name

Student Id/Name

I have sorted this list by School District, then School, then Grade, and then by Subject. So basically, all items in the list that have the exact same school district, school, grade, and subject will be next to each other in the list.

But how can I split the list into sublists based on this condition? Each sublist would have to be objects with the same school district, school, grade, and subject. I don't want a map or any other data structure - I want a list of lists/sublists.

Thanks.

1

There are 1 answers

3
Stefano Riffaldi On

given that data structures :

record ElementKey(String district, String school, String grade) {
    public ElementKey(Element element) {
        this(element.district(), element.school(), element.grade());
    }
}

record Element(String district, String school, String grade, String subject, String student) {
}

here we are:

/**
 * Assuming list is sorted by key
 *
 * @param elements list of elements to group
 * @return lists of elements grouped by key
 */
private static List<List<Element>> subList1(List<Element> elements) {
    if (elements == null || elements.isEmpty()) {
        return List.of();
    }
    List<List<Element>> listOfList = new ArrayList<>();
    listOfList.add(new ArrayList<>());
    ElementKey latestKey = new ElementKey(elements.get(0));
    for (Element element : elements) {
        ElementKey currentKey = new ElementKey(element);
        if (!currentKey.equals(latestKey)) {
            listOfList.add(new ArrayList<>());
            latestKey = currentKey;
        }
        listOfList.get(listOfList.size() - 1).add(element);
    }
    return listOfList;
}

or, much better, and no need that source list be sorted

private static List<List<Element>> subList(List<Element> elements) {
    return elements.stream()
            .collect(Collectors.groupingBy(ElementKey::new, Collectors.toList()))
            .values().stream()
            .toList();
}

I don't know if I got data structure correctly, but in the end what count is the concept:

  1. iterate over collection
  2. keep tracking of last element
  3. when change: create another sublist

Taking the term “sub list” literally, if you want a copy-free subList rather than new array lists, here the thing:

private static List<List<Element>> realSubList(List<Element> elements) {
    if (elements == null || elements.isEmpty()) {
        return List.of();
    }
    List<List<Element>> listOfList = new ArrayList<>();
    int fromIndex = 0;
    for (int i = 0; i < elements.size(); i++) {
        if (!isSameGroup(elements.get(fromIndex), elements.get(i))) {
            listOfList.add(elements.subList(fromIndex, i));
            fromIndex = i;
        }
    }
    listOfList.add(elements.subList(fromIndex, elements.size()));
    return listOfList;
}