1

I have the below working code using one value to compare but want to compare with two values. I need to add one more filter as "getAddressID" along with getAction. Could some help me to modify the below code?

List<Data> finalData = dataList.stream().collect(
    Collectors.collectingAndThen(
        Collectors.toCollection(
            () -> new TreeSet<>(Comparator.comparing(Data::getAction))),
            ArrayList::new))
public class TestCollection {

/**
 * @param args
 */
public static void main(String[] args) {
    
    List<Data> dataList = new ArrayList<Data>();
    
    Data data1 = new Data("DELIVERED", "1112");
    Data data2 = new Data("DELIVERED", "1113");
    Data data3 = new Data("PICKEUP", "1112");
    Data data4 = new Data("PICKEUP", "1112");
    
    dataList.add(data1);
    dataList.add(data2);
    dataList.add(data3);
    dataList.add(data4);
    
    //Filer by Action
    List<Data> finalData = dataList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<> (Comparator.comparing(Data::getAction))), ArrayList::new));
    
    for(Data data : finalData) {
        System.out.println(" Action " + data.getAction() + " Address ID " + data.getAddressID());
    }
    
    

}

}

The result is : "Action DELIVERED Address ID 1112" "Action PICKEUP Address ID 1112"

But I want result as: "DELIVERED Address ID 1112" "DELIVERED Address ID 1113" "PICKEUP Address ID 1112"

user3067524
  • 506
  • 4
  • 10
  • 26
  • Can you explain a bit more in detail, what your code should do actually? Maybe also add an example of input and desired output. – cyberbrain Mar 16 '22 at 17:11
  • Input is a list of Data objects. Data object has attributes inside of it. Above code is able to filetr by "Action" attrubute and returns a list. I just need to add one more filer to the code. I am going to write a smaple code and post in few minutes. – user3067524 Mar 16 '22 at 17:15
  • Does your `Data` class only have the two fields `addressID & action` or are there more? Are both fields strings like shown in your code or was is just an example? – Eritrean Mar 16 '22 at 17:59
  • It has more values but I just want to filter by two values and both are String values. Above is an example. – user3067524 Mar 16 '22 at 18:01
  • 1
    Have you tried adding a second comparator, ie: Comparator.comparing(Data::getAction).thenComparing(Comparator.comparing(Data::otherMethod) – Zeke Rogers Mar 16 '22 at 18:15
  • 1
    @Zeke Rogers. That's it. It worked. Thank you so much.Pleas put that as a solution. I will accept it. – user3067524 Mar 16 '22 at 18:21

2 Answers2

2

You can chain your comparators using thenComparing, ie

new TreeSet<>(Comparator.comparing(Data::getAction).thenComparing(Comparator.comparing(Data::otherMethod))
Zeke Rogers
  • 146
  • 6
0

If I get it right, you are trying to get distinct items from your list by the combination of the two properties addressID & action. Here is a an accepted answer, thanks to @Stuart Marks, which works to get distinct elements by one property. That could be further modified to solve your use case like:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;


....

List<Data> dataList = // your data objects

List<Data> distinctData = dataList.stream()
            .filter(distinctByKeys(Data::getAction, Data::getAddressID))
            .collect(Collectors.toList());

where the method distinctByKeys could look like

private static <T> Predicate<T> distinctByKeys(final Function<? super T, ?>... keyExtractors) {
    final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
    return t -> {
        final List<?> keys = Arrays.stream(keyExtractors)
                .map(ke -> ke.apply(t))
                .collect(Collectors.toList());
        return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
}
Eritrean
  • 15,851
  • 3
  • 22
  • 28