使用Java lambdas累积答案计数

使用Java lambdas累积答案计数,java,lambda,Java,Lambda,我有表单数据项列表,每个数据项包含类型为Map的数据字段。字符串键是动态字段名(调查问题),值是为该特定问题回答的选项 在列表之外,我如何计算每个字段名(问题)的唯一答案数,我假设是这样的映射您应该从一个流开始,这样我就可以避免将FormDataDTO对象收集到一个列表中。更改此项: List<FormDataDTO> dtos = dataEntries.stream().map(mapper::dataEntryToDto).collect(Collectors.toList()

我有表单数据项列表,每个
数据项
包含类型为
Map
的数据字段。字符串键是动态字段名(调查问题),值是为该特定问题回答的选项


在列表之外,我如何计算每个字段名(问题)的唯一答案数,我假设是这样的<代码>映射您应该从一个流开始,这样我就可以避免将FormDataDTO对象收集到一个列表中。更改此项:

List<FormDataDTO> dtos = dataEntries.stream().map(mapper::dataEntryToDto).collect(Collectors.toList());
如果希望计数是整数而不是长数值,可以使用
collecting然后
转换每个长数值:

Map<String, Map<String, Integer>> answerCountsByField =
    dtos.flatMap(dto -> dto.getData().entrySet().stream()).collect(
        Collectors.groupingBy(e -> e.getKey(),
            Collectors.groupingBy(e -> e.getValue(),
                Collectors.collectingAndThen(
                        Collectors.counting(), Long::intValue))));
Map answerCountsByField=
flatMap(dto->dto.getData().entrySet().stream()).collect(
Collectors.groupingBy(e->e.getKey(),
Collectors.groupingBy(e->e.getValue(),
收藏,收藏,然后(
Collectors.counting(),Long::intValue));

我现在写了一些东西,这个结构实际上适用于这两种情况,因为最终结果可能完全相同,唯一的区别是解析,因为我必须深入一个映射。我对函数也做了同样的尝试,然后我必须检查类型where is
entry.getValue()
,因为在一种情况下,它是字符串,在另一种情况下,它是另一个映射,但它看起来确实很糟糕

我将分组逻辑放入测试中,在这里发布,数据包括两种类型的结构化数据、单个值和对象

也许你可以建议这里有什么改进

  @Test
  public void multiValueLists_answerCountsByField() throws Exception {

    List<DataEntry> entries = new ArrayList<DataEntry>() {
      {
        add(new DataEntry("{field1000: {1: true, 2: true, 3: true}, field2000: {1: true, 2: true}, field3000: '1', field4000: '1', field5000: '1'}"));
        add(new DataEntry("{field1000: {1: true, 2: true, 3: false}, field2000: {1: true, 2: false}, field3000: '1', field4000: '2', field5000: '2'}"));
        add(new DataEntry("{field1000: {1: false, 2: true, 3: true}, field2000: {1: true}, field3000: '1', field4000: '2', field5000: '3'}"));
      }
    };

    Stream<FormDataDTO> dtoStream = entries.stream().map(mapper::dataEntryToDto);

    Map<String, Map<String, AtomicInteger>> answers = new LinkedTreeMap<>();

    dtoStream.forEach(dto -> dto.getData().entrySet()
      .forEach(field -> {
        answers.putIfAbsent(field.getKey(), new LinkedTreeMap<>());
        Map<String, AtomicInteger> values = answers.get(field.getKey());

        if (field.getValue() instanceof Map)
          ((Map<String, Boolean>) field.getValue()).entrySet().stream()
            .filter(value -> Boolean.TRUE.equals(value.getValue()))
            .forEach(value -> {
              values.putIfAbsent(value.getKey(), new AtomicInteger());
              values.get(value.getKey()).incrementAndGet();
            });
        else {
          values.putIfAbsent(field.getValue().toString(), new AtomicInteger());
          values.get(field.getValue().toString()).incrementAndGet();
        }
      }));

    assertThat(field(answers, "field1000"), is("[1=2, 2=3, 3=2]"));
    assertThat(field(answers, "field2000"), is("[1=3, 2=1]"));
    assertThat(field(answers, "field3000"), is("[1=3]"));
    assertThat(field(answers, "field4000"), is("[1=1, 2=2]"));
    assertThat(field(answers, "field5000"), is("[1=1, 2=1, 3=1]"));
  }
@测试
public void multiValueLists_answerCountsByField()引发异常{
列表条目=新的ArrayList(){
{
添加(新数据项(“{field1000:{1:true,2:true,3:true},field2000:{1:true,2:true},field3000:'1',field4000:'1',field5000:'1'}”);
添加(新数据项(“{field1000:{1:true,2:true,3:false},field2000:{1:true,2:false},field3000:'1',field4000:'2',field5000:'2'}”);
添加(新数据项({field1000:{1:false,2:true,3:true},field2000:{1:true},field3000:'1',field4000:'2',field5000:'3'}”);
}
};
Stream dtoStream=entries.Stream().map(mapper::dataEntryToDto);
Map answers=新建LinkedTreeMap();
dtoStream.forEach(dto->dto.getData().entrySet())
.forEach(字段->{
putIfAbsent(field.getKey(),newlinkedtreemap());
映射值=answers.get(field.getKey());
if(field.getValue()instanceof Map)
((映射)field.getValue()).entrySet().stream()
.filter(value->Boolean.TRUE.equals(value.getValue()))
.forEach(值->{
value.putIfAbsent(value.getKey(),new-AtomicInteger());
value.get(value.getKey()).incrementAndGet();
});
否则{
values.putIfAbsent(field.getValue().toString(),new-AtomicInteger());
value.get(field.getValue().toString()).incrementAndGet();
}
}));
资产(字段(回答为“字段1000”)为(“[1=2,2=3,3=2]”);
资产(字段(回答为“field2000”)为(“[1=3,2=1]”);
资产(字段(答案为“字段3000”)为(“[1=3]”);
资产(字段(答案为“field4000”)为(“[1=1,2=2]”);
资产(字段(回答为“field5000”)为(“[1=1,2=1,3=1]”);
}

您可能正在寻找reduce操作或自定义收集器。我尝试了许多不同的方法,但缺乏所有函数的知识,无法找到合适的解决方案。也许我会感谢一些示例代码的帮助。哇,这似乎真的很有效,我甚至还没接近。非常感谢:)@Vaelyr似乎。。。如果我是你,我会担心的;试着去理解它的作用:圆周率学习了一下,玩了一会儿,不用担心,现在明白了:)再次感谢。@fge我倾向于同意。我个人会使用老式的循环,所以我会在一个月后看到我能理解的代码。@VGR我赞同你的评论:p有一种说法,代码就是WORM(写一次,读很多),我完全同意;)
{"field6696":{"1":true,"2":true},"field7994":{"1":true,"2":false,"3":false,"4":true}}
{"field6696":{"1":false,"2":true},"field7994":{"1":false,"2":true,"3":true}}
{"field6696":{"1":false,"2":true},"field7994":{"1":false,"2":true,"3":false,"4":true}}
{"field6696":{"1":false,"2":true,"3":true},"field7994":{"1":true,"2":true,"3":false}}
{"field6696":{"1":false,"2":true},"field7994":{"1":true,"2":true,"3":true,"4":true}}
List<FormDataDTO> dtos = dataEntries.stream().map(mapper::dataEntryToDto).collect(Collectors.toList());
Stream<FormDataDTO> dtos = dataEntries.stream().map(mapper::dataEntryToDto);
Map<String, Map<String, Long>> answerCountsByField =
    dtos.flatMap(dto -> dto.getData().entrySet().stream()).collect(
        Collectors.groupingBy(e -> e.getKey(),
            Collectors.groupingBy(e -> e.getValue(),
                Collectors.counting())));
Map<String, Map<String, Integer>> answerCountsByField =
    dtos.flatMap(dto -> dto.getData().entrySet().stream()).collect(
        Collectors.groupingBy(e -> e.getKey(),
            Collectors.groupingBy(e -> e.getValue(),
                Collectors.collectingAndThen(
                        Collectors.counting(), Long::intValue))));
  @Test
  public void multiValueLists_answerCountsByField() throws Exception {

    List<DataEntry> entries = new ArrayList<DataEntry>() {
      {
        add(new DataEntry("{field1000: {1: true, 2: true, 3: true}, field2000: {1: true, 2: true}, field3000: '1', field4000: '1', field5000: '1'}"));
        add(new DataEntry("{field1000: {1: true, 2: true, 3: false}, field2000: {1: true, 2: false}, field3000: '1', field4000: '2', field5000: '2'}"));
        add(new DataEntry("{field1000: {1: false, 2: true, 3: true}, field2000: {1: true}, field3000: '1', field4000: '2', field5000: '3'}"));
      }
    };

    Stream<FormDataDTO> dtoStream = entries.stream().map(mapper::dataEntryToDto);

    Map<String, Map<String, AtomicInteger>> answers = new LinkedTreeMap<>();

    dtoStream.forEach(dto -> dto.getData().entrySet()
      .forEach(field -> {
        answers.putIfAbsent(field.getKey(), new LinkedTreeMap<>());
        Map<String, AtomicInteger> values = answers.get(field.getKey());

        if (field.getValue() instanceof Map)
          ((Map<String, Boolean>) field.getValue()).entrySet().stream()
            .filter(value -> Boolean.TRUE.equals(value.getValue()))
            .forEach(value -> {
              values.putIfAbsent(value.getKey(), new AtomicInteger());
              values.get(value.getKey()).incrementAndGet();
            });
        else {
          values.putIfAbsent(field.getValue().toString(), new AtomicInteger());
          values.get(field.getValue().toString()).incrementAndGet();
        }
      }));

    assertThat(field(answers, "field1000"), is("[1=2, 2=3, 3=2]"));
    assertThat(field(answers, "field2000"), is("[1=3, 2=1]"));
    assertThat(field(answers, "field3000"), is("[1=3]"));
    assertThat(field(answers, "field4000"), is("[1=1, 2=2]"));
    assertThat(field(answers, "field5000"), is("[1=1, 2=1, 3=1]"));
  }