Reputation: 553
I have a specific problem I am trying to solve, and need some assistance with.
I have a POJO with three fields:
This POJO can be populated from the DB or an external endpoint and will return a list of said POJO. Now we end up with two seperate lists with similar infomation. These lists will be unordered and varying in size (sometimes empty).
How can I combine these two lists to create a single unique list based on the ID?
My solution is very cumbersome and ugly to read - it involved two nested loops and 3 pairs of if-else statements. Is there a better way, more efficient way of doing it.
Here is some sample code:
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class Scrap2 {
public static void main(String[] args) {
MyEntity one = new MyEntity("A", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity two = new MyEntity("B", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity three = new MyEntity("C", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity four = new MyEntity("D", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
List<MyEntity> listOne = Arrays.asList(one, two, three, four);
MyEntity aaa = new MyEntity("A", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity bbb = new MyEntity("B", new Date(2020 - 1900, Calendar.OCTOBER, 25), 20);
MyEntity ccc = new MyEntity("D", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);
MyEntity ddd = new MyEntity("E", new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);
List<MyEntity> listTwo = Arrays.asList(aaa, bbb, ccc, ddd);
for (MyEntity listItem : combineTwoLists(listOne, listTwo)) {
System.out.println(listItem);
}
}
private static List<MyEntity> combineTwoLists(List<MyEntity> listOne, List<MyEntity> listTwo) {
return Arrays.asList(listOne.get(0), listTwo.get(1), listOne.get(2), listTwo.get(2), listTwo.get(3));
}
}
class MyEntity {
private final String id;
private final Date date;
private final Integer weightedNumber;
public MyEntity(String id, Date date, Integer weightedNumber) {
this.id = id;
this.date = date;
this.weightedNumber = weightedNumber;
}
@Override
public String toString() {
return "MyEntity{" +
"id='" + id + '\'' +
", date=" + date +
", weightedNumber=" + weightedNumber +
'}';
}
public String getId() {
return id;
}
public Date getDate() {
return date;
}
public Integer getWeightedNumber() {
return weightedNumber;
}
}
The output should be something like this (can be unordered/unsorted):
MyEntity{id='A', date=Fri Sep 25 2020, weightedNumber=10}
MyEntity{id='B', date=Sun Oct 25 2020, weightedNumber=20}
MyEntity{id='C', date=Fri Sep 25 2020, weightedNumber=10}
MyEntity{id='D', date=Fri Sep 25 2020, weightedNumber=20}
MyEntity{id='E', date=Fri Sep 25 2020, weightedNumber=20}
Upvotes: 5
Views: 1755
Reputation: 34460
Here's another way to do this, without streams, yet functional:
Map<String, MyEntity> map = new LinkedHashMap<>();
Consumer<MyEntity> addAction = e -> map.merge(
e.getId(),
e,
BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
.thenComparing(MyEntity::getWeightedNumber)));
listOne.forEach(addAction);
listTwo.forEach(addAction);
List<MyEntity> result = new ArrayList<>(map.values());
This iterates both lists and executes the addAction
consumer on each element. The addAction
consumer consists of merging entities in a map, by applying the Map.merge
method.
Upvotes: 1
Reputation: 18440
You can join both lists first then get the max by date then weightedNumber using Comparator
with BinaryOperator.maxBy
for the same id to create a map using Collectors.toMap
.Then takes the value of the map in new ArrayList.
return new ArrayList<MyEntity>(
Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.toMap(MyEntity::getId, Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
.thenComparing(MyEntity::getWeightedNumber))))
.values());
Upvotes: 5
Reputation: 19545
The following approach using Collectors.groupingBy
and Collectors.maxBy
should work:
groupingBy
date
and by weightedNumber
using maxBy
collector.Map.Entry<String, Optional<MyEntity>>
to List<MyEntity>
private static List<MyEntity> combineTwoLists(List<MyEntity> listOne, List<MyEntity> listTwo) {
return Stream.concat(listOne.stream(), listTwo.stream())
.collect(Collectors.groupingBy(MyEntity::getId,
Collectors.maxBy(
Comparator.comparing(MyEntity::getDate)
.thenComparing(MyEntity::getWeightedNumber))
)).entrySet()
.stream() // Stream<String, Optional<MyEntity>>
.map(Map.Entry::getValue) // take optional value
.map(Optional::get) // get from optional
.collect(Collectors.toList());
}
Upvotes: 2