如何通过两个对象属性将一个对象中的列表分组?
我正在使用Drools 7.9.0和Java8。我有一个Result
类,用于返回每个匹配的Drools规则。
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
public class Result implements Serializable {
private Integer id;
private String name;
private List<Occurrences> occurrences = new ArrayList<>();
public void addOccurrence(Occurrences occurrence) {
this.occurrences.add(occurrence);
}
}
执行Drools之后,我得到一个List<Result>
。转换为JSON看起来像这样。
当前结果:
[
{
name: "Whatever"
id: 0001,
occurrences: [{
(...)
}]
},
{
name: "Whatever"
id: 0001,
occurrences: [{
(...)
}]
},
{
name: "Whatever"
id: 0002,
occurrences: [{
(...)
}]
},
{
name: "Other thing"
id: 0002,
occurrences: [{
(...)
}]
}
]
我需要对Result
的列表进行分组,这样ocurrences
被id
和name
分组,就像这样。
预期结果:
[
{
name: "Whatever"
id: 0001,
occurrences: [{
(...)
}, {
(...)
}]
},
{
name: "Whatever"
id: 0002,
occurrences: [{
(...)
}]
},
{
name: "Other thing"
id: 0002,
occurrences: [{
(...)
}]
}
]
实现此目标的最佳方法是什么?我有两个选择:
List<Result>
已经按照我的需要进行了结构化。也许可以使用addResult
方法创建一个类,该方法检查列表中的id
和name
并将出现的事件添加到正确的条目中。但这并不理想,因为这会增加规则内部的复杂性。List<Result>
进行后处理,以便将occurrences
按id
和name
分组。但是我不知道如何以优化和简单的方式做到这一点。做第二个选择的最好方法是什么?
答案 0 :(得分:2)
您可以这样做
Map<String, List<Result>> resultsWithSameIdAndName = results.stream()
.collect(Collectors.groupingBy(r -> r.getId() + ":" + r.getName()));
List<Result> mergedResults = resultsWithSameIdAndName.entrySet().stream()
.map(e -> new Result(Integer.valueOf(e.getKey().split(":")[0]),
e.getKey().split(":")[1],
e.getValue().stream().flatMap(r -> r.getOccurrences().stream())
.collect(Collectors.toList())))
.collect(Collectors.toList());
答案 1 :(得分:2)
第二个选项的简单方法可能是:
Map<String, List<Occurences>> resultsMapped = new HashMap<>();
List<Result> collect = results.forEach(r -> resultsMapped.merge(String.format("%s%d", r.name, r.id),
r.occurrences, (currentOccurences, newOccurences) -> {
currentOccurences.addAll(newOccurences);
return currentOccurences;
});
在地图上存储您的事件的发生,键是名称和ID并置在字符串中。此密钥尚未优化。
答案 2 :(得分:2)
您也可以为此使用一些流。这将按ID和名称进行分组,然后为每个组创建一个新的Result对象,其中包含该组的所有匹配项。
Collection<Result> processed = results.stream().collect(Collectors.groupingBy(r -> r.getId() + r.getName(),
Collectors.collectingAndThen(Collectors.toList(),
l -> new Result(l.get(0).getId(), l.get(0).getName(),
l.stream().flatMap(c -> c.getOccurrences().stream()).collect(Collectors.toList()))
))).values();
这需要将AllArgsConstructor和更多的getter添加到Result类,但是您可以根据需要进行调整。 在此示例中,用于分组的密钥并不安全。
答案 3 :(得分:2)
我将以下方法添加到Result
:
Result addOccurrences (List<Occurrences> occurrences) {
this.occurrences.addAll (occurrences);
return this;
}
然后您可以使用流:
List<Result> collect = results.stream ()
.collect (Collectors.groupingBy (Result::getId, Collectors.groupingBy (Result::getName, Collectors.toList ())))
.values ()
.stream ()
.map (Map::values)
.flatMap (Collection::stream)
.map (Collection::stream)
.map (resultStream -> resultStream.reduce ((r0, r1) -> r0.addOccurrences (r1.getOccurrences ())))
.filter (Optional::isPresent)
.map (Optional::get)
.collect (Collectors.toList ());