Arka
Arka

Reputation: 101

How to convert Generic List with Predicate Interface into a Lambda Expression?

I am learning Java 8 Functional Interface and was trying out some examples. I am trying to create a method which will accept Generic List as one argument and a String data filter argument as another. Below code is working as expected, but when I am trying to convert Predicate into Lambda Expression, then I am struggling.

@SuppressWarnings("unchecked")
public static <T> List<T> filter_and_find_only_selected_Data1(List<T> genericList, String dataFilter){
    Stream<List<T>> list = genericList.stream().map(eachListObj-> {
            if(eachListObj instanceof Employee){
                return genericList.stream().filter((Predicate<? super T>) new Predicate<Employee>() {
                    public boolean test(Employee eachEmpObj) {
                        return eachEmpObj.getEmpDept().equalsIgnoreCase(dataFilter);
                    }
                }).collect(Collectors.toList());
            }else if(eachListObj instanceof Customer){
                return genericList.stream().filter((Predicate<? super T>) new Predicate<Customer>(){
                    public boolean test(Customer eachCust) {
                        return !eachCust.getCustomerName().equalsIgnoreCase(dataFilter);
                    }
                }).collect(Collectors.toList());
            }
            return null;
    });
    return list.findAny().get();
}

Is there any way, I can convert the Predicate into Lambda as well as if there a way, I can convert if-else-if into Ternary Operator. Like: (if condition)?return Value:(else-if condition):return value:null;

Upvotes: 2

Views: 850

Answers (3)

Holger
Holger

Reputation: 298233

I think, you actually want something like this:

public static <T> List<T> filter_and_find_only_selected_Data(
    List<T> list, Function<? super T, String> stringProperty, String filterValue) {

    return list.stream()
        .filter(t -> filterValue.equalsIgnoreCase(stringProperty.apply(t)))
        .collect(Collectors.toList());
}

Then, the caller can use

List<Employee> source = …;
List<Employee> filtered
    = filter_and_find_only_selected_Data(source, Employee::getEmpDept, "value");

or

List<Customer> source = …;
List<Customer> filtered
    = filter_and_find_only_selected_Data(source, Customer::getCustomerName, "Bob");

or

List<File> source = Arrays.asList(new File("foo", "bar"), new File("foo", "test"),
    new File("xyz"), new File("TEST"), new File("abc", "bar"), new File("bla", "Test"));
List<File> filtered = filter_and_find_only_selected_Data(source, File::getName, "test");

to demonstrate the flexibility of a truly generic method.

Upvotes: 3

Nikolas
Nikolas

Reputation: 44398

The generics doesn't help you much here since Customer and Employee seem not mutually compatible. As long as you want to use a generic type <T>, you have to assure that this type is consistent across all the method scope execution. All you can do is using the explicit cast.

I'd start with a static Map extracting a mapping function based on the incoming Class<?>. The Function<Object, String> results in String as long as you wish to compare these with dataFilter:

static Map<Class<?>, Function<Object, String>> exctractionMap() {
    Map<Class<?>, Function<Object, String>> map = new HashMap<>();
    map.put(Customer.class, item -> Customer.class.cast(item).getCustomerName());
    map.put(Employee.class, item -> Employee.class.cast(item).getEmpDept());
    return map;
}

Putting this static map aside for a while, I think your whole stream might be simplified anyway. This should work together:

static List<String> findSelectedData(List<?> genericList, String dataFilter) {
    return genericList.stream()                     // Stream<Object>
        .map(item -> exctractionMap()               // Stream<String> using the function
            .get(item.getClass())                   // ... get through Class<Object>
            .apply(item))                           // ... applied Function<Object,String>
        .filter(s-> s.equalsIgnoreCase(dataFilter)) // Stream<String> equal to dataFilter
        .collect(Collectors.toList());              // List<String>
}

A note: Please, respect the Java conventions and name the method filterAndFindOnlySelectedData1.

Upvotes: 0

PatrickChen
PatrickChen

Reputation: 1420

Why not put all in the filter? try this

return genericList.stream().filter(item -> 
                    (item instanceof Customer && ((Customer) item).getCustomerName().equalsIgnoreCase(dataFilter)
                    || (item instanceof Employee && ((Employee) item).getEmpDept().equalsIgnoreCase(dataFilter))))
                    .collect(Collectors.toList());

or extract a function for this filter

public boolean isAllow(T item, String dataFilter) {
return (item instanceof Customer && ((Customer) item).getCustomerName().equalsIgnoreCase(dataFilter))
                    || (item instanceof Employee && ((Employee) item).getEmpDept().equalsIgnoreCase(dataFilter)))
}
//then use it in filter
return genericList.stream().filter(item -> isAllow(item, dataFilter)
                    .collect(Collectors.toList());

Hope it helps

Upvotes: 0

Related Questions