4

Question: I have some collection List which need to be filtered with below condition in Java 8,

  1. Arrange the object in the list in a descending order of date (where date can be null as well)
  2. Iterate each elements of the list one-by-one and validate some condition, if condition matches, then break the iteration their only and return all the elements(till the condition matches) and ignore the remaining elements.

Code snippet:

  public class Employee {

    private int empId;

    private String empName;

    private Date empDob;
    
    //Constructor
    //Getter Setter
    
}

Test Class:

public static void main(String[] args) {

    List<Employee> asList = Arrays.asList(new Employee(1, "Emp 1", getDate(1998)),
            new Employee(2, "Emp 2", getDate(2005)), new Employee(3, "Emp 3", getDate(2000)),
            new Employee(4, "Emp 4", null), new Employee(5, "Emp 5", getDate(1990)),
            new Employee(6, "Emp 6", new Date()));

    List<Employee> result = asList.stream()
            .sorted(Comparator.comparing(Employee::getEmpDob, Comparator.nullsLast(Comparator.reverseOrder())))
            .filter(i -> i.getEmpName() != null && !StringUtils.isEmpty(i.getEmpName())
                    && i.getEmpName().equals("Emp 3"))
            .collect(Collectors.toList());
    System.out.println(result);

}

public static Date getDate(int year) {
    Calendar c1 = Calendar.getInstance();
    c1.set(year, 01, 01);
    return c1.getTime();

}

The above code which I tried is returning only the element which is matches with the condition and result is:

[Employee [empId=3, empName=Emp 3, empDob=Wed Feb 01 15:55:57 MYT 2000]]

Expected Output which I need to do:

The result which I am expecting is something like below (it should collect all the elements till the condition matches)

[Employee [empId=6, empName=Emp 6, empDob=Sat Aug 15 16:06:22 MYT 2020], 
     Employee [empId=2, empName=Emp 2, empDob=Tue Feb 01 16:06:22 MYT 2005], 
     Employee [empId=3, empName=Emp 3, empDob=Tue Feb 01 16:06:22 MYT 2000]]
Sudarshan
  • 82
  • 9

3 Answers3

4

I'd suggest the solutions of Picking elements of a list until condition is met with Java 8 Lambdas

  1. Do your own Accumulator and Collector

    result = asList.stream().collect(ArrayList::new, (l, e) -> {
        if (l.isEmpty() || !l.get(l.size() - 1).getEmpName().equals("Emp 3"))
            l.add(e);
    }, (l1, l2) -> {
        if (l1.isEmpty() || !l1.get(l1.size() - 1).getEmpName().equals("Emp 3"))
            l1.addAll(l2);
    });
    
  2. REQUIRES JAVA>=9: Use Stream.takeWhile() to get all the element before stopping then retrieve the element to stop on

     // Add all element until the first Emp3
     List<Employee> result = asList.stream()
         .sorted(comparing(Employee::getEmpDob, nullsLast(reverseOrder())))
         .takeWhile(i -> i.getEmpName() != null && !i.getEmpName().isEmpty()
             && !i.getEmpName().equals("Emp 3"))
         .collect(Collectors.toList());
    
     // Add the first Emp3
     asList.stream()
         .sorted(comparing(Employee::getEmpDob, nullsLast(reverseOrder())))
         .filter(i -> i.getEmpName() != null && !i.getEmpName().isEmpty()
             && i.getEmpName().equals("Emp 3"))
         .findFirst().ifPresent(result::add);
    

None of them is very nice, but seems there no nice way to it

azro
  • 53,056
  • 7
  • 34
  • 70
  • For Java 9+ solution, we need to add negative condition else list will be empty as first condition (Emp 6) itself becomes false – Pankaj Aug 15 '20 at 09:52
  • Okay, I didn't notice ! in `!i.getEmpName().equals("Emp 3")` – Pankaj Aug 15 '20 at 15:34
1

You should get date of selected employee, and then filter all with date bigger or equals his date, and then sort them.

List<Employee> asList = Arrays.asList(new Employee(1, "Emp 1", getDate(1998)),
            new Employee(2, "Emp 2", getDate(2005)), new Employee(3, "Emp 3", getDate(2000)),
            new Employee(4, "Emp 4", null), new Employee(5, "Emp 5", getDate(1990)),
            new Employee(6, "Emp 6", new Date()));

    Date searchedEmpDate = asList.stream()
            .filter(i -> i.getEmpName() != null && !i.getEmpName().isEmpty()
                    && i.getEmpName().equals("Emp 3")).findAny().get().getEmpDob();

    List<Employee> result = asList.stream()
            .filter(i -> i.getEmpDob() != null && i.getEmpDob().compareTo(searchedEmpDate) >= 0)
            .sorted(Comparator.comparing(Employee::getEmpDob, Comparator.nullsLast(Comparator.reverseOrder())))
            .collect(Collectors.toList());
    System.out.println(result);
Andrew Blagij
  • 236
  • 1
  • 7
1

Java 9+

In java 9+ you can use takeWhile() as -

List<Employee> result = asList.stream()
            .sorted(Comparator.comparing(Employee::getEmpDob, Comparator.nullsLast(Comparator.reverseOrder())))
            .takeWhile(Predicate.not(i -> (i.getEmpName() != null && !StringUtils.isEmpty(i.getEmpName())
                    && i.getEmpName().equals("Emp 3"))))
            .collect(Collectors.toList());

Note use of Predicate.not, this will evaluate expression till condition is met (i.e. till "Emp 3" is NOT encountered. List<Employee> result doesn't include "Emp 3". To add "Emp 3"

asList.stream()
                .sorted(Comparator.comparing(Employee::getEmpDob, Comparator.nullsLast(Comparator.reverseOrder())))
                .filter(i -> i.getEmpName() != null && !StringUtils.isEmpty(i.getEmpName())
                        && i.getEmpName().equals("Emp 3"))
                .collect(Collectors.toCollection(() -> result));

Java 8+

You can implement your own takeWhile as described here or you can simulate takeWhile as (this method takes sorted list)

public static <T> List<T> takeWhile(List<T> list, Predicate<T> p) {
        int i = 0;
        for (T item : list) {
            if (!p.test(item)) {
                return list.subList(0, i);
            }
            i++;
        }
        return list;
    }

and call as -

List<Employee> employees = takeWhile(asList.stream()
                .sorted(Comparator.comparing(Employee::getEmpDob, Comparator.nullsLast(Comparator.reverseOrder())))
                .collect(Collectors.toList()), i -> i.getEmpName() != null && !StringUtils.isEmpty(i.getEmpName())
                && i.getEmpName().equals("Emp 3"));
Pankaj
  • 2,220
  • 1
  • 19
  • 31