A table about employees has following data: employee Id, employee name, their manager's Id and their team members' Id. Each employee would have multiple entries based on their team size. Say an employee with 5 other members in their team would have five rows with the employee and manager id repeated each with one team member's id.
Sample table:
employeeId | employeeName | managerId | teamEmployeeId |
-------------------------------------------------------
1000 | Alex | 4000 | 1101 |
1200 | Bran | 4100 | 1301 |
1200 | Bran | 4100 | 1302 |
1000 | Alex | 4000 | 1102 |
1200 | Bran | 4100 | 1303 |
1000 | Alex | 4000 | 1103 |
1200 | Bran | 4100 | 1304 |
1200 | Bran | 4100 | 1305 |
1000 | Alex | 4000 | 1104 |
The goal is to split each employees' team IDs (with the manager id too) into a separate array to be used later.
Expected result:
allIds:
1000
1200
teamIds for "Alex" :
1101
1102
1103
1104
4000
teamIds for "Bran" :
1301
1302
1303
1304
1305
4100
The first part of getting unique employee ids is working (based on this answer). But trying to split the team members by using the employee id returns the first value, but the correct number of times. Say: a team with 5 returns an array of five with the first member's id in all. Manager's id isn't being added.
The code I am using:
List<ViewData> list = getDataList();
String[] allIds = list.parallelStream()
.map(ViewData::getId).distinct().toArray(String[]::new);
System.out.println(allIds.length + "\n");
for (String id : allIds) {
String[] teamIds = list.parallelStream()
.filter(row -> row.getId().equals(id))
.map(ViewData::getTeamId).distinct()
.toArray(String[]::new);
teamIds = Arrays.copyOf(teamIds, teamIds.length+1 );
teamIds[teamIds.length] = list.parallelStream()
.filter(obj -> obj.getId().equals(id))
.map(ViewData::getManagerId).toString();
System.out.println(teamIds.length + "\n");
}
I understand this is a logical error. All the docs I have referred for filter() shows the syntax is correct. My understanding is the filter() returns the entire row whose id matches with the one I am looping through and the map() takes out the team member's id from that row and finally everything is returned as a string array.
Where have I gone wrong? Writing the code or understanding how these functions work?
edit:
if a table like this causes duplicates/repeat rows(particularly the exact number of time it is supposed to be present):
a view without a primary key (i.e., no guaranteed unique column ) needs a composite primary key derived by combining two columns that would then be unique.
Create a separate @Embeddable
class for composite key combo
Add it to main model class:
@EmbeddedId private UniqueId uniqueId;
Proceed with the exisiting logic as in the answer:
for (String id : allIds) {
String[] teamIds = list.stream()
.filter(row -> row.getUniqueId().getId().equals(id))
.map(obj -> obj.getUniqueId().getTeamEmployeeId())
.toArray(String[]::new);
teamIds = Arrays.copyOf(teamIds, teamIds.length + 1);
teamIds[teamIds.length - 1] = list.stream()
.filter(obj -> obj.getUniqueId().getId().equals(id))
.map(ViewData::getManagerId).findFirst().orElse("");
String empName = list.stream()
.filter(obj -> obj.getUniqueId().getId().equals(id))
.map(ViewData::getName).findFirst().orElse("");
}
Update:
This was a terribly complicated solution from a limited understanding of data structures. A simpler (slightly redundant) solution using Set's auto de-duplication:
Map<Integer, Set<Integer>> res = new HashMap<>();
employeeDetails.forEach(o-> {
Integer empId = o.empId;
if(!res.containsKey(empId)) res.put(empId, new HashSet<>());
res.get(empId).addAll(Set.of(o.teamMemberId, o.managerId));
});
return res;
Even this isn't a very optimal solution. It makes a lot of access and modification to the res
map and its values. But in contrast to the previous approach this only goes through the list of records once. Some basic bench marking on both solutions shows the newer approach is 3-4 times faster for record of 1000 size and 10x for size in 100s.
But going 5000+ brings both closer as they both get bogged down by the number of objects created to store the results and their access.
Based on the question and comments, what you need is:
for (String id : allIds) {
String[] teamIds = list.parallelStream()
.filter(row -> row.getId().equals(id))
.map(ViewData::getTeamId).distinct()
.toArray(String[]::new);
teamIds = Arrays.copyOf(teamIds, teamIds.length + 1);
teamIds[teamIds.length - 1] = list.parallelStream()
.filter(obj -> obj.getId().equals(id))
.map(ViewData::getManagerId)
.findFirst()
.orElse(null);
System.out.println(teamIds.length + "\n");
}