I have a list of data like the one given below:
List<Data> data = new ArrayList<Data>();
data.add(new Data("d1", "option1"));
data.add(new Data("d2", "option1"));
data.add(new Data("d1", "option2"));
data.add(new Data("d3", "option1"));
data.add(new Data("d3", "option2"));
data.add(new Data("d3", "option3"));
The structure looks like this:
class Data {
private String name;
private String option;
private List<String> options = new ArrayList<>();
public Data(String name, String option) {
this.name = name;
this.option = option;
}
public void addOption(String option) {
options.add(option);
}
}
How to group the items to a new array based on the name with its options,
[
"d1": {
"name": "d1",
"options": ["option1", "option2"]
},
"d2": {
"name": "d2",
"options": ["option1"]
},
"d3": {
"name": "d3",
"options": ["option1", "option2", "option3"]
}
]
The Collectors. groupingBy() method in Java 8 now permits developers to perform GROUP BY operation directly. GROUP BY is a SQL aggregate operation that is quite useful. It enables you to categorise records based on specified criteria.
The groupingBy() method of Collectors class in Java are used for grouping objects by some property and storing results in a Map instance. In order to use it, we always need to specify a property by which the grouping would be performed. This method provides similar functionality to SQL's GROUP BY clause.
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.
Just use Stream. of() to create a stream from a bunch of object references. Besides regular object streams Java 8 ships with special kinds of streams for working with the primitive data types int , long and double . As you might have guessed it's IntStream , LongStream and DoubleStream .
You can use a Collectors.toMap
collector:
Map<String,Data>
grouped = data.stream()
.collect(Collectors.toMap(Data::getName,
d -> new Data(d.getName(),d.getOption()),
(d1,d2) -> {d1.addOptions(d2.getOptions()); return d1;});
This will require changing the Data
constructor to add the passed option to the options List
as well as adding an addOptions
method that receives a list of options and adds all of them to the options List
of the current instance.
Try this out,
final Map<String, List<String>> optionsByName = dataList.stream().collect(
Collectors.groupingBy(Data::getName, Collectors.mapping(Data::getOption, Collectors.toList())));
I would suggest you to use this map
as a source to create the DTO you need, without cluttering your code to get the exact result you want.
You can use a simple grouping collector on the stream:
Map<String, List<String>> optionMap = data.stream()
.collect(Collectors.groupingBy(Data::getName,
Collectors.mapping(Data::getOption, Collectors.toList())));
When tested with your test list, the above produces this output:
{d1=[option1, option2], d2=[option1], d3=[option1, option2, option3]}
The above seems like a better, type-safe alternative to your desired map. Additionally, it avoids unnecessary duplication.
Your final map can nonetheless be computed based on the above:
Map<String, Map<String, Object>> m = new HashMap<>();
optionMap.entrySet().forEach(entry -> {
Map<String, Object> map = new HashMap<>();
map.put("name", entry.getKey());
map.put("options", entry.getValue());
m.put(entry.getKey(), map);
});
The m
map above looks like:
{d1={name=d1, options=[option1, option2]},
d2={name=d2, options=[option1]},
d3={name=d3, options=[option1, option2, option3]}}
This is what you need, but it seems that it contains duplicated data, and it's less type-safe than the simple map in the previous result.
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