Reputation: 23
I have a List<Employee>
e which I want to convert into
Map<String, Map<String,Emp>>
where the outer string should be "Name" and the inner string should be "Domain".
Name Id Domain
e(0) - Emp1, 1, Insurance
e(1) - Emp1, 2, Sales
e(2) - Emp2, 3, Sales
e(3) - Emp4, 4, Marketing
I am tried the following-
e.stream().collect(Collectors.groupingBy(
Employee::getName,
toMap(Employee::getDomain,Emp)));
So the expected output map should look like
<Emp1>
<Insurance, e(0)>
<Sales, e(1)>
<Emp2>
<Sales, e(2)>
<Emp4>
<Marketing, e(3)>
But I get only unique values, actual output-
<Emp1>
<Insurance, e(0)>
<Emp2>
<Sales, e(2)>
<Emp4>
<Marketing, e(3)>
Can someone tell the best way to do it?
Upvotes: 2
Views: 335
Reputation: 31878
What you are mostly looking for is nested grouping such as :
Map<String, Map<String, List<Employee>>> groupedMap = employees.stream()
.collect(Collectors.groupingBy(Employee::getName,
Collectors.groupingBy(Employee::getDomain, Collectors.toList())));
Note - The values are List<Employee>
which are the employees grouped by name and then by domain. (Both same clubbed into a List.)
If you were to stricly adhere to getting a single employee corresponding to the specified grouping, the code pretty much works for me with a small modification:
Map<String, Map<String, Employee>> groupedReducedMap = employees.stream()
.collect(Collectors.groupingBy(Employee::getName,
Collectors.toMap(Employee::getDomain,
Function.identity(), // value as the employee instance
(a, b) -> a))); // choose first instance for similar 'domain'
Upvotes: 1
Reputation: 393821
Since the output should be Map<String, Map<String,Employee>>
and not Map<String, Map<String,List<Employee>>>
(i.e. based on your requested output, there cannot be two Employee
s having the same name and the same domain), you can chain two groupingBy
and then use reducing
to ensure that each inner group will have a single Employee
instead of a List<Employee>
:
Map<String, Map<String,Optional<Employee>>> output =
e.stream()
.collect(Collectors.groupingBy(Employee::getName,
Collectors.groupingBy(Employee::getDomain,
Collectors.reducing((x1,x2)->x2))));
The problem with this version of reducing
is that it returns an Optional<Employee>
instead of Employee
, even though we know the Optional
will never be empty.
We get can around that with:
Map<String, Map<String,Employee>> output =
e.stream()
.collect(Collectors.groupingBy(Employee::getName,
Collectors.groupingBy(Employee::getDomain,
Collectors.reducing(e.get(0),
(x1,x2)->x2))));
Now, we use a variant of reducing
having an identity value, to which we pass an arbitrary Employee
instance (it doesn't matter which, since it will always be replaced by the correct instance).
Upvotes: 0