Reputation: 339
I have a class like this.
public class Foo {
private String prefix;
private String sector;
private int count;
}
Given a foo list:
//Args: prefix, sector, count
fooList.add(new Foo("44",,"RowC", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));
fooList.add(new Foo("1",,"RowB", 1 ));
fooList.add(new Foo("1",,"Rowa", 1 ));
And I need to return the request an object sorted by Prefix asc like this:
{
"1": {
"Rowa": "2",
"RowB": "1"
},
"44": {
"RowC": "1"
}
}
So the problem is: I have to group the list by the prefix, and then show, every sector and the count(*) of items on the list with the same row and sector. The far that I got is using stream like this:
fooList.stream()
.collect(Collectors.groupingBy(
Foo::getPrefix,
Collectors.groupingBy(
Foo::getSector,
Collectors.mapping(Foo::getSector , Collectors.counting())
)
));
The problem is, that the code above, is that the count is a Long, and I need to return as a String. I've tried with .toString but it gives me an error (Can assign java.lang.String to java.util.stream.Collector).
UPDATE
With the help of Andreas and Naman, now I can map count as String. I just need it sorted by Prefix.
Can anyone help me?
Upvotes: 2
Views: 773
Reputation: 156
Is this what you need?
Code :
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;
public class Main {
public static void main(String [] args){
List<Foo> fooList = new ArrayList<>();
fooList.add(new Foo("44", "RowC", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));
fooList.add(new Foo("1", "RowB", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));
Map<String, Map<String, Integer>> result = grouper(fooList);
result.forEach( (k,v) -> System.out.printf("%s\n%s\n", k,v) );
}
/* group the list by the prefix, and then show, every sector and the
* count(*) of items on the list with the same row and sector.*/
public static Map<String, Map<String, Integer>> grouper(List<Foo> foos){
//Map<prefix, Map<sector, count>
Map<String, Map<String, Integer>> result = foos.stream()
.collect(
//Step 1 - group by prefix. Outer map key is prefix.
groupingBy( Foo::getPrefix,
//Use a tree map which wil be sorted by its key, i.e prefix.
TreeMap::new,
//Step 2 - group by sector.
groupingBy( Foo::getSector,
//Step 3 - Sum the Foo's in each sector in each prefix.
summingInt(Foo::getCount)
)
)
);
return result;
}
}
Output :
1
{Rowa=2, RowB=1}
44
{RowC=1}
PS - I referred to this tutorial to answer your question. I referred to the examples in "2.5. Grouping by Multiple Fields" and "2.7. Getting the Sum from Grouped Results". The next step was to find out how to also order by the key of a map while grouping which I got from "2.11. Modifying the Return Map Type" and also checked it here.
Upvotes: 0
Reputation: 159225
You were almost there, just replace the Collectors.mapping
line with:
Collectors.summingInt(Foo::getCount))
As in:
List<Foo> fooList = new ArrayList<>();
fooList.add(new Foo("44", "RowC", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));
fooList.add(new Foo("1", "RowB", 1 ));
fooList.add(new Foo("1", "Rowa", 1 ));
Map<String, Map<String, String>> result = fooList.stream().collect(
Collectors.groupingBy(
Foo::getPrefix,
TreeMap::new, // remove if prefix sorting not needed
Collectors.groupingBy(
Foo::getSector,
() -> new TreeMap<>(Collator.getInstance()), // remove if sector sorting not needed
Collectors.collectingAndThen(
Collectors.summingInt(Foo::getCount),
String::valueOf
)
)
)
);
System.out.println(result); // prints: {1={Rowa=2, RowB=1}, 44={RowC=1}}
Notice the TreeMap
constructors added to the groupingBy()
calls, which ensures that the maps are sorted. The first is sorting lexicographically, while the second sorts according to spoken language, i.e. upper- vs lowercase doesn't affect order.
Upvotes: 2