使用流和收集器创建嵌套映射

Creating a nested Map using Streams and Collectors

class QuizAnswers {
  List<MultipleChoiceAnswer> multipleChoiceAnswers;
  List<FreeResponseAnswer> freeResponseAnswers; //not relevant to this question
}

class MultipleChoiceAnswer {
  int questionId;
  // The index of the selected multiple choice question
  int answer_selection;
}

我函数的输入是 List<QuizAnswers>

我想创建映射 <MultipleChoiceAnswer.questionId : <MultipleChoiceAnswer.answer_selection, total count of answer_selection>Map<Integer, Map<Integer, Long>> 的输出。换句话说,我想创建一个嵌套映射,将每个多项选择测验问题映射到一个表示该测验问题每个答案选择的总选择数的映射。

假设输入 List<QuizAnswers> quizAnswersList 为:

[ {questionId: 1, answer_selection: 2},    
  {questionId: 1, answer_selection:2},  
  {questionId: 1, answer_selection:3},   
  {questionId: 2, answer_selection:1} ]

那么我希望输出为:

{1 : {2:2, 3:1}, 2: {1, 1}}

因为 Id = 1 的问题在答案选择 21 的答案选择 3 上有两个选择,而 Id=2 的问题有1 选择答案选项 1

我试过了

quizAnswersList.stream()
            .map(
                quizAnswers ->
                    quizAnswers.getMultipleChoiceAnswers().stream()
                        .collect(
                            Collectors.groupingBy(
                                MultipleChoiceAnswer::getQuestionId,
                                Collectors.groupingBy(
                                    MultipleChoiceAnswer::getAnswerSelection,
                                    Collectors.counting()))));

这给我一个错误。我对一般的流和收集器不是很熟悉,所以我很想学习如何正确地做到这一点。

input to my function is a List<QuizAnswers>. I want to create an output of Map<Integer, Map<Integer, Long>>

I have tried quizAnswersList.stream().map(quizAnswers -> ... ) which is giving me an error.

方法map()是一个中间操作,即它产生一个。因此,如果您尝试将您列出的 stream-statement 分配给类型为 Map 的变量,则会出现编译错误。

流管道需要以终端操作结束,如 collect 才能执行并产生结果。

并且在 collect() 之前,您必须应用 flatMap(),它需要 stream 作为参数,以转换 QuizAnswers 的流进入 MultipleChoiceAnswer.

您对 collectors 的使用是正确的,不需要任何更改。

public static void main(String[] args) {
    List<QuizAnswers> quizAnswersList =
        List.of(new QuizAnswers(List.of(new MultipleChoiceAnswer(1, 2),
                                        new MultipleChoiceAnswer(1, 2))),
                new QuizAnswers(List.of(new MultipleChoiceAnswer(1, 3),
                                        new MultipleChoiceAnswer(2, 1))));

    Map<Integer, Map<Integer, Long>> totalCountOfAnswerSelectionByQuestion =
        quizAnswersList.stream()
            .flatMap(quizAnswers -> quizAnswers.getMultipleChoiceAnswers().stream())
            .collect(Collectors.groupingBy(MultipleChoiceAnswer::getQuestionId,
                        Collectors.groupingBy(MultipleChoiceAnswer::getAnswerSelection,
                            Collectors.counting())));

    System.out.println(totalCountOfAnswerSelectionByQuestion);
}

输出

{1={2=2, 3=1}, 2={1=1}}

I want to create an output of Map<Integer, Map<Integer, Long>> that maps <MultipleChoiceAnswer.questionId : <MultipleChoiceAnswer.answer_selection>, total count of answer_selection>.

你很接近。您只是没有 flatMapMultipleChoiceAnswers 放到流中,所以您有一个嵌套流,这就是导致问题的原因。

根据您编辑的问题,这是我想出的。

List<MultipleChoiceAnswer> mca =
        List.of(new MultipleChoiceAnswer(1, 2),
                new MultipleChoiceAnswer(1, 2),
                new MultipleChoiceAnswer(1, 3),
                new MultipleChoiceAnswer(2, 1));

// more could be added to the List.  You only provided one.
List<QuizAnswers> list = List.of(new QuizAnswers(mca));
  • flatMap 所有 MultipleChoice 列表
  • questionId
  • 分组
  • 然后根据 AnswerSelection 对它们进行分组并得到一个计数
  • 然后你得到你请求的地图输出。
Map<Integer,Map<Integer,Long>> map = list.stream()
        .flatMap(s -> s.getMultipleChoiceAnswers().stream())
        .collect(Collectors.groupingBy(
                MultipleChoiceAnswer::getQuestionId,
                Collectors.groupingBy(
                        MultipleChoiceAnswer::getAnswerSelection,
                        Collectors.counting())));

map.entrySet().forEach(System.out::println);

打印

1={2=2, 3=1}
2={1=1}

问题

  • 您想如何处理多个 QuizAnswer 实例?
  • 您想如何处理多个 MulitpleChoiceAnswer 列表。你只提供了每一种。

它们可以全部flatmapped在一起并按上述方式处理。但我认为答案可能存在一些差异(可能是针对不同的测试),您不希望将这些差异分组并算作相同。

例子

如果我将以下内容添加到 List<QuizAnswers>

List<MultipleChoiceAnswer> mca2 =
List.of(new MultipleChoiceAnswer(1, 2),
        new MultipleChoiceAnswer(1, 2),
        new MultipleChoiceAnswer(5, 2),
        new MultipleChoiceAnswer(5, 2));

并使用上述解决方案进行处理,输出将是

1={2=4, 3=1}
2={1=1}
5={2=2}