Search code examples
javajava-stream

Complex sorting over objects and their nested members using Streams


I have a Arraylist of answers, each answer contains either none or many answerValues. AnswerValues have an id.

What I would like to achieve is the following:

  1. Traverse every answer and sort their answerValues by id ascending.
  2. Sort answers by the smallest id of one of their answerValues.

enter image description here

What I've tried so far to address 2. is:

laneValues.setAnswers(laneValues.getAnswers().stream()
                    .sorted((e1, e2) -> e1.getAnswerValues().get(0).getId().compareTo(e2.getAnswerValues().get(0).getId())).toList());

Problem:

  • the .get(0) throws an NPE if answer has no answerValues.
  • Requires sorting the answerValues by their id for every answer first.

What would the ideal solution look like?


Solution

  • I think i have found a suitable solution for my problem now. But I have separated the sorting into three steps:

    First thing I did is to sort the answerValues by ID:

    for (SrmQsRfiAnswer answer: laneValues.getAnswers()) {
        List <SrmQsRfiAnswerValue> answerValues = answer.getAnswerValues();
        answerValues.sort(Comparator.comparingLong(SrmQsRfiAnswerValue::getId));
    }
    

    Now all answerValues of every answer are sorted by ID. Then I put all answers to a HashMap with the answer ID as key.

    Map <Long, List< SrmQsRfiAnswer>> answerMap = laneValues.getAnswers().stream()
        .collect(Collectors.groupingBy(SrmQsRfiAnswer::getId));
    

    And in the final step, I sort the answers for each key in the Map by the lowest ID of the answerValues with a custom "comparator":

    answerMap.values().forEach(listOfAnswers - > listOfAnswers.sort(Comparator.comparingLong(SrmQsRfiAnswer::getMinAnswerValueId)));
    
    public class SrmQsRfiAnswer {
    ...
        public Long getMinAnswerValueId() {
            if (answerValues == null || answerValues.isEmpty()) {
                return Long.MAX_VALUE; // Return a high value for null or empty lists
            }
            return answerValues.stream()
                .mapToLong(SrmQsRfiAnswerValue::getId)
                .min()
                .orElse(Long.MAX_VALUE);
        }
    }