I have a List<Object[]> where the the Object[] has size 3 and comes from a query. The result of the query is as follows:
| vehicleId | modelId | serviceId |
|------------------------------------|---------|-----------------|
|93bf3e92-7a37-4e23-836d-eed5a104341f| 214|80a7-ce5640b18879|
|b4066520-e127-44b7-bcc0-1d1187de559c| 214|80a7-ce5640b18879|
|ae6cb0fe-1501-4311-a2b4-cfb8b4f51ca4| 214|80a7-ce5640b18879|
|cf80ff11-6e23-4c19-8b6d-55d34d131566| 214|80a7-ce5640b18879|
It should be mapped in the List below. The second and last columns will be mapped to modelId and serviceId whilst the first column should become a list of vehicleIds.
I need to map it into a List where MyDTO is as follows:
MyDTO{
// constructor
MyDTO(String modelId, String serviceId, List<String> vehicleIds){...}
String modelId;
String serviceId;
List<String> vehicleIds;
}
I am trying to figure out how to group by in a stream but nothing seems to come out. That's where I'm blocked...
listOfObjectArrays.stream()
.map(objects -> new MyDTO((String) objects[0], (String) objects[1], null));
Can't figure out how to apply a reduce operation that does the job, any help really appreciated!
Edit: Sorry I forgot to mention that I'm stuck with Java 8. Thank you all for the great answers.
CodePudding user response:
You can create a nested intermediate map by grouping your data by modelId
and then serviceId
using groupingBy()
and mapping()
collectors.
And then create a stream over entry set. And flatten each inner map creating new MyDTO
based on every combination of modelId
and serviceId
.
Map<String, Map<String, List<String>>> vehicleIdByModelIdAndServiceId =
listOfObjectArrays.stream()
.collect(Collectors.groupingBy(objects -> (String) objects[1],
Collectors.groupingBy(objects -> (String) objects[2],
Collectors.mapping(objects -> (String) objects[0],
Collectors.toList()))));
List<MyDTO> result = vehicleIdByModelIdAndServiceId.entrySet().stream()
.flatMap(baseEntry -> baseEntry.getValue().entrySet().stream()
.map(entry -> new MyDTO(baseEntry.getKey(), entry.getKey(), entry.getValue())))
.collect(Collectors.toList());
Another option is to use a Map.Entry
as a key in the intermediate map and the value will be a list of vehicleId
.
List<MyDTO> result = listOfObjectArrays.stream()
.collect(Collectors.groupingBy(objects -> Map.entry((String) objects[1], (String) objects[2]),
Collectors.mapping(objects -> (String) objects[0],
Collectors.toList())))
.entrySet().stream()
.map(entry -> new MyDTO(entry.getKey().getKey(),
entry.getKey().getValue(),
entry.getValue()))
.collect(Collectors.toList());
CodePudding user response:
You could map them all to Strings first and the group by the tuple (modelId, serviceId) and only then map the results of the grouping to the dtos.
List<MyDto> myDtos = queryResult.stream()
.map(objects -> List.of((String) objects[0], (String) objects[1], (String) objects[2])
.collect(Collectors.groupingBy(r -> List.of(r.get(0), r.get(1)))
.entrySet().stream()
.map(entry -> new MyDto(entry.getKey().get(0), entry.getKey().get(1), entry.getValue()))
.collect(toList());
Instead of the list in the first map I'd use a record named ResultRow
, if you are on JDK14 I'd recommend that to increase readability or solve it declaratively instead.
CodePudding user response:
If you don't necessarily need to use stream
, you can always use a standard cycle, for example:
Map<String, MyDTO> myDTOs = new HashMap<>();
for(Object[] a : listOfObjectArrays) {
String key = a[1] " - " a[2];
MyDTO myDTO = myDTOs.get(key);
if(myDTO == null) {
List<String> vehicleIds = new ArrayList<>();
vehicleIds.add((String) a[0]);
myDTO = new MyDTO((String) a[1], (String) a[2], vehicleIds);
myDTOs.put(key, myDTO);
} else {
myDTO.getVehicleIds().add((String) a[0]);
}
}
List<MyDTO> myDTOList = new ArrayList<>(myDTOs.values());
CodePudding user response:
The idea is to
- Stream
Object[]
as{String,Long,String}
- Group By
{Object[0],Object[1]}
; Collect(DistinctObject[3]
) Map<{Object[0],Object[1]},List<Object[3]>>
-->List<MyDto>
And with a heads-on approach, I can build something like this:
Map<Set<Object[],List<String>> map = myListOfObjArr
.stream()
.collect(groupingBy(
o->new Object [] {o[1],o[2]}, // group by last two only
mapping(
o->(String)o[0], // Collapse the vehicle id's to a list
toList() // Using set to take out duplicate vehicle id's
)
));
// Convert the map to a List.
List<Object[]> list = map.entrySet().stream()
.map(e->new MyDTO(e.getKey()[0],e.getKey()[1],(List)e.getValue())
.collect(toList());
CodePudding user response:
This needs a Collectors.groupBy() function since you're taking a stream and grouping them together.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class F {
public static void main(String[] args) {
var in = List.of(new Object[]{"93bf3e92-7a37-4e23-836d-eed5a104341f", "214", "80a7-ce5640b18879"},
new Object[]{"b4066520-e127-44b7-bcc0-1d1187de559c", "214", "80a7-ce5640b18879"},
new Object[]{"b4066520-e127-44b7-bcc0-dddddddddddd", "215", "80a7-ce5640b18879"}
);
Map<Key, List<MyDTOOne>> p1 =
in.stream()
.map(objs -> new String[]{(String) objs[0], (String) objs[1], (String) objs[2]})
.map(objs -> new MyDTOOne(objs[1], objs[2], objs[0]))
.collect(Collectors.groupingBy(myDTO -> new Key(myDTO.modelId, myDTO.serviceId)));
List<MyDTO> p2 = p1.entrySet()
.stream()
.map(e -> new MyDTO(e.getKey().modelId, e.getKey().serviceId, e.getValue().stream().map(MyDTOOne::vehicleId).toList()))
.toList();
System.out.println(p2);
}
static record Key(String modelId, String serviceId) {
}
static record MyDTO(String modelId, String serviceId, List<String> vehicleIds) {
}
static record MyDTOOne(String modelId, String serviceId, String vehicleId) {
}
}