简体   繁体   中英

Grouping objects to list using lambda

I'm having problems because my select is returning several lines of result due to an Options object that has several different options, and I needed to group this result in java.

I have the parent class which is the QuestionEventDTO:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class QuestionEventDTO {
    
    public Long id;
    public QuestionDTO question;
    public List<QuestionDTO> questions;

}

And I have the daughter class:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class QuestionDTO {
    
    public Long id;
    public OptionDTO option;
    public List<OptionDTO> options;

}

And I have the granddaughter class:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class OptionDTO {
    
    public Long id;
    public String description;

}

After hitting the procedure, I set the response fields:

public class QuestionEventMapper implements RowMapper<QuestionEventDTO> {

    @Override
    public QuestionEventDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
        QuestionEventDTO dto = new QuestionEventDTO();
        dto.setId(rs.getLong(1));
        dto.setQuestion(new QuestionDTO(rs.getLong(2), new QuestionOptionDTO(rs.getLong(3), rs.getString(4)), null));
        
        return dto;
    }

}
QuestionEventDTO [id=1, question= QuestionDTO [id=1, option= OptionDTO [id=1, description="Are you Dev?"], options = []], questions =[]]
QuestionEventDTO [id=1, question= QuestionDTO [id=1, option= OptionDTO [id=2, description="What's rating?"], options = []], questions =[]]
QuestionEventDTO [id=2, question= QuestionDTO [id=2, option= OptionDTO [id=3, description="What's your dog's name?"], options = []], questions =[]]
QuestionEventDTO [id=2, question= QuestionDTO [id=2, option= OptionDTO [id=4, description="Are you Dev?"], options = []], questions =[]]

But I would like it to stay like this:

QuestionEventDTO [id=1, question= null, questions = QuestionDTO [id=1, option= null, options = [OptionDTO [id=1, description="Are you Dev?"], OptionDTO [id=2, description="What's rating?"]]]]
QuestionEventDTO [id=2, question= null, questions = QuestionDTO [id=2, option= null, options = [OptionDTO [id=3, description="What's your dog's name?"], OptionDTO [id=4, description="Are you Dev?"]]]]

I don't have much experience in lambda, I would like a solution to this problem, could someone help me?

I'll give it a try.

WARNING | Beware that this is only a POC and not good readable code.

What you need is a method, that takes a List<QuestionEventDTO> and transforms it to another List<QuestionEventDTO> .

It is possible to do it with streams and nested streams in the map-methods.

Here is the steps this streams will take:


  1. Group the QuestionDTO by the ID of the QuestionEventDTO 's
    • Input: List<QuestionEventDTO>
    • Output: Map<Long, List<QuestionEventDTO>>
  2. Transform the grouped Lists to Map<Long, List<QuestionDTO>
    • Input: Map<Long, List<QuestionEventDTO>>
    • Output: Map<Long, Map<Long, List<QuestionDTO>>>
  3. Transform the grouped QuestionDTO to a Map<Long, OptionDTO>
    • Input: Map<Long, Map<Long, List<QuestionDTO>>>
    • Output: Map<Long, Map<Long, Map<Long, OptionDTO>>>

  1. Transform the grouped objects to new QuestionEventDTO where the Lists are filled.

Here is what the code would look like (heavy usage of Collectors.groupingBy and Collectors.toMap ):

private static List<QuestionEventDTO> transform(List<QuestionEventDTO> events) {
  //Step1
  Map<Long, List<QuestionEventDTO>> groupedByQuestionEventId = events.stream()
      .collect(Collectors.groupingBy(QuestionEventDTO::getId));
  
  //Step 2
  Map<Long, Map<Long, List<QuestionDTO>>> alsoGroupedQuestionID = groupedByQuestionEventId
      .entrySet().stream()
      .collect(Collectors.toMap(Map.Entry::getKey,
          e -> e.getValue().stream()
              .map(v -> v.getQuestion())
              .collect(Collectors.groupingBy(QuestionDTO::getId)))
      );

  //Step 3
  Map<Long, Map<Long, Map<Long, OptionDTO>>> alsoGroupedByOptionID = alsoGroupedQuestionID
      .entrySet().stream()
      .collect(Collectors.toMap(Map.Entry::getKey,
          e -> e.getValue()
              .entrySet().stream()
              .collect(Collectors.toMap(Map.Entry::getKey,
                  v -> v.getValue().stream()
                      .map(QuestionDTO::getOption)
                      .collect(Collectors.toMap(OptionDTO::getId, t -> t))))));

  //Step 4
  List<QuestionEventDTO> transformedBack = alsoGroupedByOptionID.entrySet().stream()
      .map(e -> {
        QuestionEventDTO eventDTO = new QuestionEventDTO();
        eventDTO.setId(e.getKey());

        List<QuestionDTO> questions = e.getValue().entrySet().stream()
            .map(q -> {
              QuestionDTO questionDTO = new QuestionDTO();
              questionDTO.setId(q.getKey());
              questionDTO.setOptions(new ArrayList<>(q.getValue().values()));
              return questionDTO;
            }).collect(Collectors.toList());

        eventDTO.setQuestions(questions);
        return eventDTO;
      })
      .collect(Collectors.toList());

  return transformedBack;
}

Of course you don't need the variables for each step and you also could combine step 2 and 3. Then the code would look like this (not very readable):

private static List<QuestionEventDTO> transformShorter(List<QuestionEventDTO> events) {
  return events.stream()
      .collect(Collectors.groupingBy(QuestionEventDTO::getId))
      .entrySet().stream()
      .collect(Collectors.toMap(Map.Entry::getKey,
          e -> e.getValue().stream()
              .map(QuestionEventDTO::getQuestion)
              .collect(Collectors.groupingBy(QuestionDTO::getId))
              .entrySet().stream()
              .collect(Collectors.toMap(Map.Entry::getKey, 
                  q -> q.getValue().stream()
                      .map(QuestionDTO::getOption)
                      .collect(Collectors.toList()))))
      )
      .entrySet().stream()
      .map(e -> {
        QuestionEventDTO eventDTO = new QuestionEventDTO();
        eventDTO.setId(e.getKey());

        List<QuestionDTO> questions = e.getValue().entrySet().stream()
            .map(q -> {
              QuestionDTO questionDTO = new QuestionDTO();
              questionDTO.setId(q.getKey());
              questionDTO.setOptions(q.getValue());
              return questionDTO;
            }).collect(Collectors.toList());

        eventDTO.setQuestions(questions);
        return eventDTO;
      })
      .collect(Collectors.toList());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM