按多个属性对对象内的列表进行分组:Java 8
Group a list inside an object by multiple attributes : Java 8
如何按两个对象属性对对象内的列表进行分组?
我正在使用 Drools 7.9.0 和 Java 8。我有一个 Result
class 用作每个匹配的 Drools 规则的 return。
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: [{
(...)
}]
}
]
实现这个的最佳方法是什么?我有两个选择:
- 更改 Drools 规则,以便我的
List<Result>
已经按照我需要的方式构建。也许用 addResult
方法创建一个 class 来检查 id
和 name
的列表,并将匹配项添加到正确的条目中。但这并不理想,因为它会增加规则内部的复杂性。
- Post-处理我的
List<Result>
,因此它将 occurrences
按 id
和 name
分组。但我不知道如何以优化和简单的方式做到这一点。
执行第二个选项的最佳方法是什么?
你可以这样做,
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());
第二个选项的简单方法可以是:
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 连接在一个字符串中。此键未优化。
您也可以为此使用一些流。这将按 ID 和名称分组,然后为每个组创建一个新的结果对象,其中包含该组的所有实例。
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 添加到结果 class,以及一些更多的 getter,但您可以根据需要进行调整。
用于分组的密钥在此示例中不安全。
我会在 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 ());
如何按两个对象属性对对象内的列表进行分组?
我正在使用 Drools 7.9.0 和 Java 8。我有一个 Result
class 用作每个匹配的 Drools 规则的 return。
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: [{
(...)
}]
}
]
实现这个的最佳方法是什么?我有两个选择:
- 更改 Drools 规则,以便我的
List<Result>
已经按照我需要的方式构建。也许用addResult
方法创建一个 class 来检查id
和name
的列表,并将匹配项添加到正确的条目中。但这并不理想,因为它会增加规则内部的复杂性。 - Post-处理我的
List<Result>
,因此它将occurrences
按id
和name
分组。但我不知道如何以优化和简单的方式做到这一点。
执行第二个选项的最佳方法是什么?
你可以这样做,
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());
第二个选项的简单方法可以是:
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 连接在一个字符串中。此键未优化。
您也可以为此使用一些流。这将按 ID 和名称分组,然后为每个组创建一个新的结果对象,其中包含该组的所有实例。
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 添加到结果 class,以及一些更多的 getter,但您可以根据需要进行调整。 用于分组的密钥在此示例中不安全。
我会在 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 ());