将两个列表合并以生成一个唯一列表(基于特定字段逻辑)

huangapple go评论81阅读模式
英文:

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(&quot;A&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity two = new MyEntity(&quot;B&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity three = new MyEntity(&quot;C&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity four = new MyEntity(&quot;D&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
List&lt;MyEntity&gt; listOne = Arrays.asList(one, two, three, four);
MyEntity aaa = new MyEntity(&quot;A&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 10);
MyEntity bbb = new MyEntity(&quot;B&quot;, new Date(2020 - 1900, Calendar.OCTOBER, 25), 20);
MyEntity ccc = new MyEntity(&quot;D&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);
MyEntity ddd = new MyEntity(&quot;E&quot;, new Date(2020 - 1900, Calendar.SEPTEMBER, 25), 20);
List&lt;MyEntity&gt; listTwo = Arrays.asList(aaa, bbb, ccc, ddd);
for (MyEntity listItem : combineTwoLists(listOne, listTwo)) {
System.out.println(listItem);
}
}
private static List&lt;MyEntity&gt; combineTwoLists(List&lt;MyEntity&gt; listOne, List&lt;MyEntity&gt; 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 &quot;MyEntity{&quot; +
&quot;id=&#39;&quot; + id + &#39;\&#39;&#39; +
&quot;, date=&quot; + date +
&quot;, weightedNumber=&quot; + weightedNumber +
&#39;}&#39;;
}
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=&#39;A&#39;, date=Fri Sep 25 2020, weightedNumber=10} 
MyEntity{id=&#39;B&#39;, date=Sun Oct 25 2020, weightedNumber=20} 
MyEntity{id=&#39;C&#39;, date=Fri Sep 25 2020, weightedNumber=10} 
MyEntity{id=&#39;D&#39;, date=Fri Sep 25 2020, weightedNumber=20} 
MyEntity{id=&#39;E&#39;, date=Fri Sep 25 2020, weightedNumber=20}

答案1

得分: 4

你可以先将这两个列表合并,然后按日期和加权数值使用ComparatorBinaryOperator.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&lt;MyEntity&gt;(
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.groupingByCollectors.maxBy应该可以工作:

  1. 将这两个列表的流连接起来。
  2. 使用groupingBy按ID对列表进行分组。
  3. 然后使用maxBy收集器依次按dateweightedNumber进行分组。
  4. 将得到的Map.Entry&lt;String, Optional&lt;MyEntity&gt;&gt;映射为List&lt;MyEntity&gt;
private static List&lt;MyEntity&gt; combineTwoLists(List&lt;MyEntity&gt; listOne, List&lt;MyEntity&gt; 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&lt;String, Optional&lt;MyEntity&gt;&gt;
                 .map(Map.Entry::getValue) // 取出可选值
                 .map(Optional::get)       // 从可选值中获取实际值
                 .collect(Collectors.toList());
}
英文:

The following approach using Collectors.groupingBy and Collectors.maxBy should work:

  1. Concatenate the streams of the two lists
  2. Group the list by ID using groupingBy
  3. And then group consequently by date and by weightedNumber using maxBy collector.
  4. Convert the resulting map of Map.Entry&lt;String, Optional&lt;MyEntity&gt;&gt; to List&lt;MyEntity&gt;
private static List&lt;MyEntity&gt; combineTwoLists(List&lt;MyEntity&gt; listOne, List&lt;MyEntity&gt; 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&lt;String, Optional&lt;MyEntity&gt;&gt;
                 .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&lt;String, MyEntity&gt; map = new LinkedHashMap&lt;&gt;();
Consumer&lt;MyEntity&gt; addAction = e -&gt; map.merge(
e.getId(), 
e, 
BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
.thenComparing(MyEntity::getWeightedNumber)));
listOne.forEach(addAction);
listTwo.forEach(addAction);
List&lt;MyEntity&gt; result = new ArrayList&lt;&gt;(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.

huangapple
  • 本文由 发表于 2020年9月25日 22:08:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/64065754.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定