我目前使用结合StreamStream API和forEach循环的方法:
public Map<String, Client> clientsWithMostPurchasesInEachCategory(Map<Client, Map<Product,Integer>> shopping) {
Map<String, Client> result = new HashMap<>();
Map<Client, Map<String, BigDecimal>> temp =
shopping.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.flatMapping(e -> e.getValue().entrySet().stream(),
Collectors.groupingBy(e -> e.getKey().getCategory(),
Collectors.mapping(ee -> ee.getKey().getPrice().multiply(BigDecimal.valueOf(ee.getValue())),
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))))));
/*curious, how could I refactor that piece of code, so the method uses only one stream chain? */
temp.forEach((client, value)
-> value.forEach((category, value1)
-> {
if (!result.containsKey(category) ||
temp.get(result.get(category)).get(category).compareTo(value1) < 0)
result.put(category, client);
}));
return result;
}
建议使用该方法的名称,我想找到一张地图Map <String, Client>
,其中包含每种产品类别中指定类别(作为键)中购买量最大(价值)的客户
购物基本上是一张地图:Map<Client, Map<Product,Integer>>
,
不确定,是否有可能? Collectors.collectingAndThen
可能有用吗?
答案 0 :(得分:1)
您可以使用 StreamEx 库,并这样做
public static Map<String, Client> clientsWithMostPurchasesInEachCategory(Map<Client, Map<Product, Integer>> shopping) {
return EntryStream.of(shopping)
.flatMapKeyValue(((client, productQuantityMap) ->
EntryStream.of(productQuantityMap)
.mapToValue((p, q) -> p.getPrice().multiply(BigDecimal.valueOf(q)))
.mapKeys(Product::getCategory)
.map(e -> new ClientCategorySpend(client, e.getKey(), e.getValue())))
)
.groupingBy(
ClientCategorySpend::getCategory,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(ClientCategorySpend::getSpend)),
t -> t.get().getClient())
);
}
答案 1 :(得分:0)
按客户分组的那一刻,您几乎注定要失败。的
顶层Collectors.groupingBy
必须使用类别作为按关键字进行分组。
要做到这一点,您需要先flatMap
再收集,这样您才能获得固定的
客户+类别+支出元素。
这是一种方法。我首先为展平流的元素定义一个POJO:
static class ClientCategorySpend
{
private final Client client;
private final String category;
private final BigDecimal spend;
public ClientCategorySpend(Client client, String category, BigDecimal spend)
{
this.client = client;
this.category = category;
this.spend = spend;
}
public String getCategory()
{
return category;
}
public Client getClient()
{
return client;
}
public BigDecimal getSpend()
{
return spend;
}
}
现在是功能:
public static Map<String, Client> clientsWithMostPurchasesInEachCategory(Map<Client, Map<Product, Integer>> shopping)
{
// <1>
Collector<? super ClientCategorySpend, ?, BigDecimal> sumOfSpendByClient = Collectors.mapping(ClientCategorySpend::getSpend,
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add));
// <2>
Collector<? super ClientCategorySpend, ?, Map<Client, BigDecimal>> clientSpendByCategory = Collectors.groupingBy(
ClientCategorySpend::getClient,
sumOfSpendByClient
);
// <3>
Collector<? super ClientCategorySpend, ?, Client> maxSpendingClientByCategory = Collectors.collectingAndThen(
clientSpendByCategory,
map -> map.entrySet().stream()
.max(Comparator.comparing(Map.Entry::getValue))
.map(Map.Entry::getKey).get()
);
return shopping.entrySet().stream()
// <4>
.flatMap(
entry -> entry.getValue().entrySet().stream().map(
entry2 -> new ClientCategorySpend(entry.getKey(),
entry2.getKey().category,
entry2.getKey().price.multiply(BigDecimal.valueOf(entry2.getValue())))
)
).collect(Collectors.groupingBy(ClientCategorySpend::getCategory, maxSpendingClientByCategory));
}
一旦有了ClientCategorySpend
(4)的流,就可以按类别对其进行分组。我用
clientSpendByCategory
收集器(2),以在客户和类别中的总支出之间建立映射。反过来,这取决于sumToSpendByClient(1),它基本上是减少费用的减速器。然后,您可以按照建议使用collectingAndThen
,
使用Map<Client, BigDecimal>
将每个max
减少到单个客户端。
答案 2 :(得分:0)
这应该做;)
public Map<String, Client> clientsWithMostPurchasesInEachCategory(Map<Client, Map<Product, Integer>> shopping) {
return shopping
.entrySet()
.stream()
.map(entry -> Pair.of(
entry.getKey(),
entry.getValue()
.entrySet()
.stream()
.map(e -> Pair.of(
e.getKey().getCategory(),
e.getKey().getPrice().multiply(
BigDecimal.valueOf(e.getValue()))))
.collect(Collectors.toMap(
Pair::getKey,
Pair::getValue,
BigDecimal::add))))
// Here we have: Stream<Pair<Client, Map<String, BigDecimal>>>
// e.g.: per each Client we have a map { category -> purchase value }
.flatMap(item -> item.getValue()
.entrySet()
.stream()
.map(e -> Pair.of(
e.getKey(), Pair.of(item.getKey(), e.getValue()))))
// Here: Stream<Pair<String, Pair<Client, BigDecimal>>>
// e.g.: entries stream { category, { client, purchase value } }
// where there are category duplicates, so we must select those
// with highest purchase value for each category.
.collect(Collectors.toMap(
Pair::getKey,
Pair::getValue,
(o1, o2) -> o2.getValue().compareTo(o1.getValue()) > 0 ?
o2 : o1))
// Now we have: Map<String, Pair<Client, BigDecimal>>,
// e.g.: { category -> { client, purchase value } }
// so just get rid of unnecessary purchase value...
.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().getKey()));
}
Pair
是org.apache.commons.lang3.tuple.Pair
。如果您不想使用Appache Commons库,则可以改用java.util.AbstractMap.SimpleEntry
。