英文:
How to collect a map of a pojo id to a set of a property of a collection on the pojo
问题
我正在尝试使用流来将POJO映射到POJO上的属性公开的项目集合。我意识到这不够清楚,所以我将展示如何在没有流的情况下完成它。
我有一个产品类别的枚举,一个产品的枚举以及一个包含产品的商店列表,如下所示:
public enum Category {
FRUIT, VEGETABLE, MEAT;
}
public enum Product {
APPLE(Category.FRUIT),
PEAR(Category.FRUIT),
BANANA(Category.FRUIT),
CARROT(Category.VEGETABLE),
POTATO(Category.VEGETABLE),
CABBAGE(Category.VEGETABLE),
MINCE(Category.MEAT),
CHOP(Category.MEAT),
STEAK(Category.MEAT);
private final Category category;
Product(Category category) {
this.category = category;
}
public String value() {
return name();
}
public static Product fromValue(String v) {
return valueOf(v);
}
public Category getCategory() {
return this.category;
}
}
public class Shop {
private long id;
private String name;
private EnumSet<Product> products;
public Shop(long id, String name, Collection<Product> products) {
this.id = id;
this.name = name;
this.products = Optional.ofNullable(products).map(EnumSet::copyOf).orElse(null);
}
}
我创建了一个方法来创建一些虚拟数据:
public static List<Shop> getListOfShops() {
Shop shop1 = new Shop(1, "Green Grocer", EnumSet.of(Product.APPLE, Product.BANANA, Product.CARROT, Product.CABBAGE));
Shop shop2 = new Shop(2, "Supermarket", EnumSet.of(Product.APPLE, Product.BANANA
, Product.CARROT, Product.CABBAGE, Product.CHOP, Product.MINCE));
Shop shop3 = new Shop(3, "Butcher", EnumSet.of(Product.STEAK));
return Arrays.asList(shop1, shop2, shop3);
}
我想要创建一个商店类别的映射:
Map<String, EnumSet<Category>> shopCategories = new HashMap<>();
其中内容如下:
{Butcher=[MEAT], Supermarket=[FRUIT, VEGETABLE, MEAT], Green Grocer=[FRUIT, VEGETABLE]}
我已经使用一些基本的循环实现了这一点,但我希望能够将其转换为流。
public static Map<String, EnumSet<Category>> getCategories(List<Shop> shops) {
Map<String, EnumSet<Category>> shopCategories = new HashMap<>();
for (Shop s : shops) {
Set<Category> cats = new HashSet<>();
for (Product p : s.getProducts()) {
cats.add(p.getCategory());
}
shopCategories.put(s.getName(), EnumSet.copyOf(cats));
}
return shopCategories;
}
我有一个模糊的想法如何将其转换为商店到产品的映射,使用终端操作进行收集。大致如下:
shops.stream().collect(Collectors.toMap(s -> s::getName, PaymentProductWithCapabilities::getProducts));
我在理解如何继续映射类别方面遇到了困难。有人可以指点我正确的方向吗?
英文:
I'm trying to use streams to get a map of a pojo to a Set of items exposed by a property on the pojo. I realise that isn't clear so I'll show how I've done it without streams.
I have and enum of product categories, and enum of Products and a list of shops with the product like this
public enum Category {
FRUIT, VEGITABLE, MEAT;
}
public enum Product {
APPLE ( Category.FRUIT ),
PEAR ( Category.FRUIT ),
BANANA ( Category.FRUIT ),
CARROT ( Category.VEGITABLE ),
POTATO ( Category.VEGITABLE ),
CABBAGE ( Category.VEGITABLE ),
MINCE ( Category.MEAT ),
CHOP ( Category.MEAT ),
STEAK ( Category.MEAT );
private final Category category;
Product( Category category ) {
this.category = category;
}
public String value() {
return name();
}
public static Product fromValue( String v ) {
return valueOf( v );
}
public Category getCategory() {
return this.category;
}
}
public class Shop {
private long id;
private String name;
private EnumSet<Product> products;
public Shop( long id, String name, Collection<Product> products ) {
this.id = id;
this.name = name;
this.products = ofNullable( products ).map( EnumSet::copyOf ).orElse( null );
}
}
I have made a method which creates some dummy data...
public static List<Shop> getListOfShops() {
Shop shop1 = new Shop( 1, "Green Grocer", EnumSet.of( Product.APPLE, Product.BANANA, Product.CARROT, Product.CABBAGE ) );
Shop shop2 = new Shop( 2, "Supermarket", EnumSet.of( Product.APPLE, Product.BANANA
, Product.CARROT, Product.CABBAGE, Product.CHOP, Product.MINCE ) );
Shop shop3 = new Shop( 3, "Butcher", EnumSet.of( Product.STEAK ) );
return Arrays.asList( shop1, shop2, shop3 );
}
I want to create a map of shop categories
Map<String, EnumSet<Category>> shopCategories = new HashMap<>();
which will have contents like this
> {Butcher=[MEAT], Supermarket=[FRUIT, VEGITABLE, MEAT], Green
> Grocer=[FRUIT, VEGITABLE]}
I have achieved this with some basic loops which I was hoping I was smart enough to convert into streams
public static Map<String, EnumSet<Category>> getCategories( List<Shop> shops ) {
Map<String, EnumSet<Category>> shopCategories = new HashMap<>();
for ( Shop s : shops ) {
Set<Category> cats = new HashSet<Category>();
for ( Product p : s.getProducts() ) {
cats.add( p.getCategory() );
}
shopCategories.put ( s.getName(), EnumSet.copyOf (cats) );
}
return shopCategories;
}
I have a vague idea how to convert it to a map of shop to products with a collector as the terminal operation. Something along the lines of
shops.stream().collect( Collectors.toMap ( s -> s::getName ,
PaymentProductWithCapabilities::getProducts ) );
I'm struggling for an entry point into understanding how to proceed to mapping the Categories instead ? Can anyone point me in the right direction ?
答案1
得分: 2
你可以使用Collectors.toMap
中的valueMapper
将你正在流式传输的商店的产品转换为预期的Map
的值,就像你的迭代风格一样:
return shops.stream()
.collect(Collectors.toMap(Shop::getName,
s -> EnumSet.copyOf(s.getProducts().stream()
.map(Product::getCategory)
.collect(Collectors.toSet()))));
要精确一点,你的实现会覆盖Map
中相同名称的两个商店的值,可以在Collectors.toMap
中使用合并函数来实现,例如(a, b) -> b
。
return shops.stream()
.collect(Collectors.toMap(Shop::getName,
s -> s.getProducts().stream()
.map(Product::getCategory)
.collect(Collectors.toCollection(
() -> EnumSet.noneOf(Category.class))),
(a, b) -> b));
英文:
You could transform the products of the shop you are streaming on, into the value of the expected Map
using the valueMapper
in Collectors.toMap
, similar to your iterative style as:
return shops.stream()
.collect(Collectors.toMap(Shop::getName,
s -> EnumSet.copyOf(s.getProducts().stream()
.map(Product::getCategory)
.collect(Collectors.toSet()))));
To be precise, your implementation overrides the value of the Map
, if the same name
is found for two shops, which in the Collectors.toMap
can be implemented with the merge function such as (a,b) -> b
.
Or better as suggested by Gene in a comments:
return shops.stream()
.collect(Collectors.toMap(Shop::getName,
s -> s.getProducts().stream()
.map(Product::getCategory)
.collect(Collectors.toCollection(
() -> EnumSet.noneOf(Category.class))),
(a, b) -> b));
答案2
得分: 1
同样的结果可以使用Collectors.groupingBy
和Collectors.flatMapping
来实现 -
Map<String, EnumSet<Category>> categories =
shops.stream()
.collect(
Collectors.groupingBy(
Shop::getName,
Collectors.flatMapping(
s -> s.getProducts().stream().map(Product::getCategory),
Collectors.toCollection(() -> EnumSet.noneOf(Category.class)))));
Collectors.groupingBy
根据 Shop::getName
对流进行分组,而 Collectors.flatMapping
则将产品列表扁平化,并使用 map
将产品映射到 Product
的类别。Collectors.flatMapping
还有一个 Collector<? super U, A, R> downstream
参数,可以用来收集 EnumSet<Category>
。
英文:
Same can be achieved using Collectors.groupingBy
and Collectors.flatMapping
as -
Map<String, EnumSet<Category>> categories =
shops.stream()
.collect(
groupingBy(
Shop::getName,
flatMapping(
s -> s.getProducts().stream().map(Product::getCategory),
toCollection(() -> EnumSet.noneOf(Category.class)))));
Collectors.groupingBy
groups stream based on Shop::getName
and Collectors.flatMapping
flattens List of Products and map
maps product to the Product
category. Collectors.flatMapping
also Collector<? super U, A, R> downstream
which can be used to collect EnumSet<Category>
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论