Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining two lists to make a unique list (based on certain field logic)

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}
like image 528
Leo Avatar asked Sep 25 '20 14:09

Leo


People also ask

How do I combine two lists without duplicates?

You can also merge lists without duplicates in Google Sheets. Select and right-click a second range that will be merged (e.g., C2:C6) and click Copy (or use the keyboard shortcut CTRL + C).

How do you combine two lists of objects?

One way to merge multiple lists is by using addAll() method of java. util. Collection class, which allows you to add the content of one List into another List. By using the addAll() method you can add contents from as many List as you want, it's the best way to combine multiple List.

How do you merge two lists without duplicates in Python?

Python merges two lists without duplicates could be accomplished by using a set. And use the + operator to merge it.

How do I combine multiple lists into one python?

In python, we can use the + operator to merge the contents of two lists into a new list. For example, We can use + operator to merge two lists i.e. It returned a new concatenated lists, which contains the contents of both list_1 and list_2.


3 Answers

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());
like image 139
Eklavya Avatar answered Oct 22 '22 16:10

Eklavya


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<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());
}
like image 21
Nowhere Man Avatar answered Oct 22 '22 15:10

Nowhere Man


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.

like image 1
fps Avatar answered Oct 22 '22 15:10

fps