Java 8 使用流,查找最新状态,如果'F'从相应的 from 状态'R'替换原因



我有一个字段名称如下的对象列表,我正在尝试替换 状态"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'

我将在下面提供我的实现,如果需要任何改进,请告诉我。

  1. 我将创建最新状态为"F"的地图 地图将有

    api-a -> api-a,F,10:30,'A3' api-c -> api-c,F,10:40,'C3'

  2. 然后我将创建地图,这将包含记录 对于先前地图中的那些记录,状态为"R"。
    上面的地图会有。

    api-a -> api-a,R,10:25,'A2'

  3. 我将从两张地图的原始列表中更改原因。

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());

相关内容

最新更新