如何获得字段的不同计数并对必须按 Java 8 中的某些字段分组的列表中的字段求和?
How to get distinct count of a field and sum a field inside a list which has to be grouped by some fields in Java 8?
我需要找到唯一名称计数,以及列表中字段值的总和
下面是我的POJO:
@Getter
@Setter
public class Orders {
private String dealId;
private String fieldId;
private String accountName;
private List<Demands> demands; //Demands contains multiple fields inside it, but I just need
//the 'amount' value mainly which is a BigDecimal.
}
List of Orders(ordersList) :
DealId | FieldId | AccountName | Demand |
1 | 11 | ABC | 100,100 |
1 | 11 | ABC | 200 |
1 | 11 | PQR | 300,100 |
1 | 12 | ABC | 100 |
2 | 21 | ABC | 200 |
2 | 21 | PQR | 300,500 |
2 | 21 | XYZ | 100 |
2 | 22 | ABC | 200,100 |
2 | 22 | ABC | 300 |
End Result :
DealId | FieldId | AccountNameCount | DemandSum |
1 | 11 | 2 | 800 |
1 | 12 | 1 | 100 |
2 | 21 | 3 | 1100 |
2 | 22 | 1 | 600 |
我需要以这种格式在某些 POJO 或集合中获取最终结果,其中
AccountNameCount 是交易的特定 FieldId 的唯一帐户名称的计数。
DemandSum 是 DealId 下特定 FieldId 的需求值的总和。
注意:Demand是Orders模型中的一个List,所以它可能有多个值,DealId下的特定FieldId的所有值都应该加上。
例如:DealId 1,FieldId 11,有2个不同的accountName,将所有需求值加到该fieldid下,dealId = 800
我正在尝试使用 Java 8 个流,已经能够按 dealId 对列表进行分组,然后是 fieldId,但我不确定如何计算 fieldId 级别的唯一帐户名称,并且需求值求和,不影响性能。
Map<String, Map<String, List<Orders>>> map = ordersList.stream()
.collect(Collectors.groupingBy(Orders::getDealId, Collectors.groupingBy(Orders::getFieldId)));
Collectors.reducing 选项可用于添加到流中以添加需求值,但由于需求是订单中的列表,因此不适用于此处。
Collectors.reducing(BigDecimal.ZERO, Demands::getAmount, BigDecimal::add);
感谢任何帮助!
也许是这样的?
return orderList.stream()
.collect(Collectors.groupingBy(o -> Arrays.asList(o.dealId, o.fieldId)))
.entrySet().stream().map(e -> {
var stats = new OrderStats();
stats.dealId = e.getKey().get(0);
stats.fieldId = e.getKey().get(1);
stats.accountNameCount = e.getValue().stream()
.map(o -> o.accountName)
.distinct()
.count();
stats.demandSum = e.getValue().stream()
.flatMap(o -> o.demands.stream())
.map(d -> d.amount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return stats;
})
.collect(Collectors.toList());
我使用 List
作为键以避免在分组 Map
中不必要的嵌套。这是可行的,因为 List
实现了基于内容的平等。
首先,让我们从按 dealId
和 fieldId
分组开始
之后我们需要的是 accountName
不同的计数和 demands
的总和,但这些在列表中可用,它在 Map
中
为了在地图上生成流,我们将使用 entrySet、
这将为我们提供 Map.Entry>
要从 Map.Entry 访问键值,我们使用方法 getKey and getValue
// group by dealId and fieldId
Map<String, Map<String, List<Orders>>> map = ordersList.stream()
.collect(Collectors.groupingBy(Orders::getDealId, Collectors.groupingBy(Orders::getFieldId)));
List<OrdersSummary> result = map
.entrySet()
.stream()
.map(Test::getOrdersSummaries)
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(result);
private static List<OrdersSummary> getOrdersSummaries(Map.Entry<String, Map<String, List<Orders>>> entry) {
return entry.getValue()
.entrySet()
.stream()
.map(e2 -> toOrdersSummary(entry.getKey(), e2))
.collect(Collectors.toList());
}
private static OrdersSummary toOrdersSummary(String dealId, Map.Entry<String, List<Orders>> entry){
long accountNameCount= entry.getValue().stream().map(Orders::getAccountName).distinct().count();
BigDecimal demandSum = entry.getValue().stream().map(Orders::getDemands)
.flatMap(List::stream)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return new OrdersSummary(dealId, entry.getKey(), accountNameCount, demandSum);
}
完整代码Demo
尽管您可以通过 java 8 流 API 获得结果(可能有点不可读)。
但是在 java 12+ 之后提供了另一个收集器(teeing),可以帮助您以更具可读性的方式编写它。
public static Collector teeing (Collector downstream1,
Collector downstream2,
BiFunction merger);
解决方案:您需要创建一些辅助对象,例如Detail
、Result
orders.stream()
.collect(groupingBy(order -> Arrays.asList(order.getDealId(), order.getFieldId()),
teeing(
// unique account names for each group
mapping(Orders::getAccountName, Collectors.toSet()),
// sum all demands amount for each group
collectingAndThen(mapping(Orders::getDemands, Collectors.toList()),
demands -> demands.stream()
.flatMap(List::stream)
.map(Demands::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add)),
// create an object by previous results
(names, demandSum) -> new Detail(names.size(), demandSum)
)
)).entrySet().stream()
.map(entry -> new Result(
entry.getKey().get(0),
entry.getKey().get(1),
entry.getValue().getCountByAccountName(),
entry.getValue().getDemandSum()))
.collect(Collectors.toList());
我需要找到唯一名称计数,以及列表中字段值的总和
下面是我的POJO:
@Getter
@Setter
public class Orders {
private String dealId;
private String fieldId;
private String accountName;
private List<Demands> demands; //Demands contains multiple fields inside it, but I just need
//the 'amount' value mainly which is a BigDecimal.
}
List of Orders(ordersList) :
DealId | FieldId | AccountName | Demand |
1 | 11 | ABC | 100,100 |
1 | 11 | ABC | 200 |
1 | 11 | PQR | 300,100 |
1 | 12 | ABC | 100 |
2 | 21 | ABC | 200 |
2 | 21 | PQR | 300,500 |
2 | 21 | XYZ | 100 |
2 | 22 | ABC | 200,100 |
2 | 22 | ABC | 300 |
End Result :
DealId | FieldId | AccountNameCount | DemandSum |
1 | 11 | 2 | 800 |
1 | 12 | 1 | 100 |
2 | 21 | 3 | 1100 |
2 | 22 | 1 | 600 |
我需要以这种格式在某些 POJO 或集合中获取最终结果,其中
AccountNameCount 是交易的特定 FieldId 的唯一帐户名称的计数。 DemandSum 是 DealId 下特定 FieldId 的需求值的总和。
注意:Demand是Orders模型中的一个List,所以它可能有多个值,DealId下的特定FieldId的所有值都应该加上。
例如:DealId 1,FieldId 11,有2个不同的accountName,将所有需求值加到该fieldid下,dealId = 800
我正在尝试使用 Java 8 个流,已经能够按 dealId 对列表进行分组,然后是 fieldId,但我不确定如何计算 fieldId 级别的唯一帐户名称,并且需求值求和,不影响性能。
Map<String, Map<String, List<Orders>>> map = ordersList.stream()
.collect(Collectors.groupingBy(Orders::getDealId, Collectors.groupingBy(Orders::getFieldId)));
Collectors.reducing 选项可用于添加到流中以添加需求值,但由于需求是订单中的列表,因此不适用于此处。
Collectors.reducing(BigDecimal.ZERO, Demands::getAmount, BigDecimal::add);
感谢任何帮助!
也许是这样的?
return orderList.stream()
.collect(Collectors.groupingBy(o -> Arrays.asList(o.dealId, o.fieldId)))
.entrySet().stream().map(e -> {
var stats = new OrderStats();
stats.dealId = e.getKey().get(0);
stats.fieldId = e.getKey().get(1);
stats.accountNameCount = e.getValue().stream()
.map(o -> o.accountName)
.distinct()
.count();
stats.demandSum = e.getValue().stream()
.flatMap(o -> o.demands.stream())
.map(d -> d.amount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return stats;
})
.collect(Collectors.toList());
我使用 List
作为键以避免在分组 Map
中不必要的嵌套。这是可行的,因为 List
实现了基于内容的平等。
首先,让我们从按 dealId
和 fieldId
之后我们需要的是 accountName
不同的计数和 demands
的总和,但这些在列表中可用,它在 Map
中
为了在地图上生成流,我们将使用 entrySet、
这将为我们提供 Map.Entry
要从 Map.Entry 访问键值,我们使用方法 getKey and getValue
// group by dealId and fieldId
Map<String, Map<String, List<Orders>>> map = ordersList.stream()
.collect(Collectors.groupingBy(Orders::getDealId, Collectors.groupingBy(Orders::getFieldId)));
List<OrdersSummary> result = map
.entrySet()
.stream()
.map(Test::getOrdersSummaries)
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(result);
private static List<OrdersSummary> getOrdersSummaries(Map.Entry<String, Map<String, List<Orders>>> entry) {
return entry.getValue()
.entrySet()
.stream()
.map(e2 -> toOrdersSummary(entry.getKey(), e2))
.collect(Collectors.toList());
}
private static OrdersSummary toOrdersSummary(String dealId, Map.Entry<String, List<Orders>> entry){
long accountNameCount= entry.getValue().stream().map(Orders::getAccountName).distinct().count();
BigDecimal demandSum = entry.getValue().stream().map(Orders::getDemands)
.flatMap(List::stream)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return new OrdersSummary(dealId, entry.getKey(), accountNameCount, demandSum);
}
完整代码Demo
尽管您可以通过 java 8 流 API 获得结果(可能有点不可读)。 但是在 java 12+ 之后提供了另一个收集器(teeing),可以帮助您以更具可读性的方式编写它。
public static Collector teeing (Collector downstream1,
Collector downstream2,
BiFunction merger);
解决方案:您需要创建一些辅助对象,例如Detail
、Result
orders.stream()
.collect(groupingBy(order -> Arrays.asList(order.getDealId(), order.getFieldId()),
teeing(
// unique account names for each group
mapping(Orders::getAccountName, Collectors.toSet()),
// sum all demands amount for each group
collectingAndThen(mapping(Orders::getDemands, Collectors.toList()),
demands -> demands.stream()
.flatMap(List::stream)
.map(Demands::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add)),
// create an object by previous results
(names, demandSum) -> new Detail(names.size(), demandSum)
)
)).entrySet().stream()
.map(entry -> new Result(
entry.getKey().get(0),
entry.getKey().get(1),
entry.getValue().getCountByAccountName(),
entry.getValue().getDemandSum()))
.collect(Collectors.toList());