Hi I have an array of objects where the category and sub category details are in the same object. So its like,
public class MyObject {
String categoryCode;
String categeoryCodeDescription;
String subCategoryCode;
String subCategoryCodeDescription;
public MyObject(String categoryCode, String categeoryCodeDescription, String subCategoryCode, String subCategoryCodeDescription) {
this.categoryCode = categoryCode;
this.categeoryCodeDescription = categeoryCodeDescription;
this.subCategoryCode = subCategoryCode;
this.subCategoryCodeDescription = subCategoryCodeDescription;
}
}
List<MyObject> collection = new ArrayList<MyObject>;
collection.add(new My Object("A1", "descA1", "A1A", "descA1A"));
collection.add(new My Object("A1", "descA1", "A1B", "descA1B"));
collection.add(new My Object("A1", "descA1", "A1C", "descA1C"));
collection.add(new My Object("A2", "descA1", "A2A", "descA2A"));
collection.add(new My Object("A2", "descA1", "A2B", "descA2B"));
Can you group by category code but map to an object including the description at the same time. So if I have two classes like..
public class Category {
String categoryCode;
String categoryDesc;
public Category (String categoryCode, String categoryDesc) {
this.categoryCode = categoryCode;
this.categoryDesc = categoryDesc;
}
}
public class SubCategory {
String subCategoryCode;
String subCategoryDesc;
public SubCategory (String subCategoryCode, String subCategoryDesc) {
this.subCategoryCode = subCategoryCode;
this.subCategoryDesc = subCategoryDesc;
}
}
..and I want to group the collection list into a Map<Category,List<SubCategory>>
. I can group on the category code but I can't see how to create a new category instance as the map key. This may not be possible in a one liner.
Map<String, List<MyObject>> map = collection.stream().collect(Collectors.groupingBy(MyObject::getCategoryCode));
In Java 8, we have the ability to convert an object to another type using a map() method of Stream object with a lambda expression. The map() method is an intermediate operation in a stream object, so we need a terminal method to complete the stream.
The groupingBy() method of Collectors class in Java are used for grouping objects by some property and storing results in a Map instance.
Stream flatMap(Function mapper) returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element. Stream flatMap(Function mapper) is an intermediate operation. These operations are always lazy.
flatMap , as it can be guessed by its name, is the combination of a map and a flat operation. That means that you first apply a function to your elements, and then flatten it. Stream. map only applies a function to the stream without flattening the stream.
It can be done if you chain a mapping
collector to groupingBy
.
You use mapping()
to transform the MyObject
instances to SubCategory
instances.
Map<Category,List<SubCategory>> map =
collection.stream().collect(Collectors.groupingBy(mo -> new Category(mo.getCategoryCode(),mo.getCategoryDesc()),
Collectors.mapping(mo->new SubCategory(mo.getSubCategoryCode(),mo.getSubCategoryDesc()),
Collectors.toList())));
Note that Category
will have to override equals
and hashCode
in order for this grouping to work.
You can do this via the toMap
collector taking a merge function:
Map<Category, List<SubCategory>> result = collection.stream()
.collect(toMap(e -> new Category(e.getCategoryCode(), e.getCategeoryCodeDescription()),
v -> new ArrayList<>(singletonList(new SubCategory(v.getSubCategoryCode(), v.getSubCategoryCodeDescription()))),
(l, r) -> { l.addAll(r); return l; }));
This assumes you Category class has overriden the equals and hashcode methods as follows:
class Category {
...
... // properties + getters + constructors etc..
...
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Category category = (Category) o;
return Objects.equals(categoryCode, category.categoryCode);
}
@Override
public int hashCode() {
return Objects.hash(categoryCode);
}
}
to better readability you could extract the keyMapper
, valueMapper
and the merge
function to different functions as follows:
private static List<SubCategory> valueMapper(MyObject v) {
return new ArrayList<>(singletonList(new SubCategory(v.getSubCategoryCode(), v.getSubCategoryCodeDescription())));
}
private static List<SubCategory> merge(List<SubCategory> l, List<SubCategory> r) {
l.addAll(r);
return l;
}
private static Category keyMapper(MyObject e) {
return new Category(e.getCategoryCode(), e.getCategeoryCodeDescription());
}
Then you can do:
Map<Category, List<SubCategory>> result = collection.stream()
.collect(toMap(Main::keyMapper,
Main::valueMapper,
Main::merge));
Where Main
is the class containing the keyMapper
, valueMapper
and merge
methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With