英文:
Combining two lists to make a unique list (based on certain field logic)
问题
import java.util.ArrayList;
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;
}
}
英文:
I have a specific problem I am trying to solve, and need some assistance with.
I have a POJO with three fields:
- ID
- Date
- Weighted Number
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?
- If the IDs are the same, choose the one which has the latest date
- If the IDs and dates are the same, choose the one with the higher weighted number
- If all are the same, it doesn't matter - choose either.
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}
答案1
得分: 4
你可以先将这两个列表合并,然后按日期和加权数值使用Comparator
和BinaryOperator.maxBy
来获取最大值,以相同的id创建一个映射,使用Collectors.toMap
。然后将映射的值取出放入新的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());
英文:
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());
答案2
得分: 2
以下方法使用Collectors.groupingBy
和Collectors.maxBy
应该可以工作:
- 将这两个列表的流连接起来。
- 使用
groupingBy
按ID对列表进行分组。 - 然后使用
maxBy
收集器依次按date
和weightedNumber
进行分组。 - 将得到的
Map.Entry<String, Optional<MyEntity>>
映射为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) // 取出可选值
.map(Optional::get) // 从可选值中获取实际值
.collect(Collectors.toList());
}
英文:
The following approach using Collectors.groupingBy
and Collectors.maxBy
should work:
- Concatenate the streams of the two lists
- Group the list by ID using
groupingBy
- And then group consequently by
date
and byweightedNumber
usingmaxBy
collector. - Convert the resulting map of
Map.Entry<String, Optional<MyEntity>>
toList<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());
}
答案3
得分: 1
这里有另一种方法可以做到这一点,不使用流,但仍然是函数式的:
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());
这会迭代两个列表,并在每个元素上执行 addAction
消费者。addAction
消费者由在映射中合并实体的操作组成,通过应用 Map.merge
方法实现。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论