使用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 isentry.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]"));
}