我有一个字段名称如下的对象列表,我正在尝试替换 状态"F"来自状态"R"的相应原因字段。仅当每个发布者的时间戳的最新状态为"F"时,才需要执行此操作
auditedBy status time Reason
api-a S 10:20 'A1'
api-a R 10:25 'A2'
api-a F 10:30 'A3'
-----
api-b S 10:30 'B1'
api-b S 10:25 'B2'
api-b S 10:20 'B3'
-----
api-c S 10:20 'C1'
api-c S 10:30 'C1'
api-c F 10:40 'C3'
所以上述数据的最终输出将是
auditedBy status time Reason
api-a S 10:20 'A1'
api-a R 10:25 'A2'
api-a F 10:30 'A2' ******
api-b S 10:30 'B1'
api-b S 10:25 'B2'
api-b S 10:20 'B3'
api-c S 10:20 'C1'
api-c S 10:30 'C1'
api-c F 10:40 'C3'
我将在下面提供我的实现,如果需要任何改进,请告诉我。
我将创建最新状态为"F"的地图 地图将有
api-a -> api-a,F,10:30,'A3' api-c -> api-c,F,10:40,'C3'
然后我将创建地图,这将包含记录 对于先前地图中的那些记录,状态为"R"。
上面的地图会有。api-a -> api-a,R,10:25,'A2'
我将从两张地图的原始列表中更改原因。
private List<DetailRecord> changeReasonFunction(List<DetailRecord> records) {
Map<String, DetailRecord> eventsFailed = records.stream()
.collect(toMap(DetailRecord::getAuditedBy, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(DetailRecord::getAuditTimestamp))))
.values()
.stream().filter(detRec -> FAILED.getStatus().equals(detRec.getStatus()))
.collect(toMap(DetailRecord::getAuditedBy, Function.identity()));
Map<String, DetailRecord> retryRecordForFailedEvents = records.stream()
.filter(rec -> eventsFailed.keySet().contains(rec.getAuditedBy()))
.filter(rec -> FAILED_RECOVERABLE_ERROR.getStatus().equals(rec.getStatus()))
.collect(toMap(DetailRecord::getAuditedBy, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(DetailRecord::getAuditTimestamp))));
records.forEach(rec -> {
if (eventsFailed.containsKey(rec.getAuditedBy()) &&
eventsFailed.get(rec.getAuditedBy()).getAuditTimestamp()
.compareTo(rec.getAuditTimestamp()) == 0) {
DetailRecord obj = retryRecordForFailedEvents.get(rec.getAuditedBy());
if (obj != null) {
rec.setExceptionReason(obj.getExceptionReason());
rec.setExceptionDetail(obj.getExceptionDetail());
}
}
});
return records;
}
你可以像这样用收集器groupingBy
试试:
private List<DetailRecord> changeReasonFunction(List<DetailRecord> records) {
return records.stream()
.collect(Collectors.groupingBy(DetailRecord::getAuditedBy))
.values().stream()
.flatMap(list -> {
Optional<DetailRecord> maxRecord = list.stream().max(Comparator.comparing(DetailRecord::getTime));
if (maxRecord.isPresent()
&& maxRecord.get().getStatus().equals("F")
&& list.stream().anyMatch(detailRecord -> detailRecord.getStatus().equals("R"))) {
maxRecord.get().setReason(
list.stream()
.filter(detailRecord -> detailRecord.getStatus().equals("R"))
.findAny().get().getReason());
}
return list.stream();
}).collect(Collectors.toList());
}
您需要根据所使用的数据类型对其进行修改。我使用过:
public class DetailRecord {
private String auditedBy;
private String status;
private Instant time;
private String reason;
}
测试:
List<DetailRecord> l = new ArrayList<>();
l.add(new DetailRecord("api-a", "S", Instant.parse("2019-01-01T10:20:00.00Z"), "A1"));
l.add(new DetailRecord("api-a", "R", Instant.parse("2019-01-01T10:25:00.00Z"), "A2"));
l.add(new DetailRecord("api-a", "F", Instant.parse("2019-01-01T10:30:00.00Z"), "A3"));
l.add(new DetailRecord("api-b", "S", Instant.parse("2019-01-01T10:30:00.00Z"), "B1"));
l.add(new DetailRecord("api-b", "S", Instant.parse("2019-01-01T10:25:00.00Z"), "B2"));
l.add(new DetailRecord("api-b", "S", Instant.parse("2019-01-01T10:20:00.00Z"), "B3"));
l.add(new DetailRecord("api-c", "S", Instant.parse("2019-01-01T10:20:00.00Z"), "C1"));
l.add(new DetailRecord("api-c", "S", Instant.parse("2019-01-01T10:30:00.00Z"), "C1"));
l.add(new DetailRecord("api-c", "F", Instant.parse("2019-01-01T10:40:00.00Z"), "C3"));
System.out.println(changeReasonFunction(l));
输出符合您的预期。
我在这个答案中选择了相同的DetailRecord
类。
一开始,我们根据auditedBy
对输入列表进行分组
Map<String, List<DetailRecord>> groupByCollection = inputList.stream()
.collect(Collectors.groupingBy(DetailRecord::getAuditedBy));
现在在每个名单上,我们都可以进行替换。列表被排序,在reduce方法中搜索status
"R"的第一条记录。如果找到,则保留并继续搜索status
"F"的记录,如果找到,则进行替换。
groupByCollection.entrySet().forEach(e -> e.getValue().stream()
.sorted(Comparator.comparing(DetailRecord::getTime))
.reduce(null, (DetailRecord a, DetailRecord v) -> {
if (a == null && v.getStatus().equals("R")) {
return v;
}
if (a != null && v.getStatus().equals("F")) {
v.setReason(a.getReason());
}
return a;
}, (a1, a2) -> a1)
);
groupByCollection.entrySet()
.forEach(e -> e.getValue().stream().forEach(System.out::println));
示例初始化列表代码:
List<String> inputStrings = Arrays.asList(
"api-a S 10:20 'A1'",
"api-a R 10:25 'A2'",
"api-a F 10:30 'A3'",
"api-b S 10:30 'B1'",
"api-b S 10:25 'B2'",
"api-b S 10:20 'B3'",
"api-c S 10:20 'C1'",
"api-c S 10:30 'C1'",
"api-c F 10:40 'C3'"
);
List<DetailRecord> inputList = inputStrings.stream()
.map(s -> s.split("\s+"))
.map(s -> new DetailRecord(s[0], s[1], Instant.parse("2019-01-01T" + s[2] + ":00.00Z"), s[3].replaceAll("'", "")))
.collect(Collectors.toList());