I have students list that I get from database. I applied predicates to List and partition the List with valid, invalid students. Now for invalid students I want to generate Map with student as a Key and error message as a value. Because I need to generate report for each student.Here what I am doing but I don't know whether it is a good approach or not or is there a better way to do it.
Actually after getting invalid students list I am trying to create a function but i Think code is getting messy and may be there is a better approach to do it. Here what I am doing
private List<Predicate<OlccStudent>> getAllPredicates() {
    List<Predicate<OlccStudent>> allPredicates = Arrays.asList(
            isValidFirstName(),
            isValidMiddleInitial(),
            isValidLastName(),
            isValidStreetAddress(),
            isValidCity(),
            isValidState(),
            isValidZip(),
            isValidDateOfBirth(),
            isValidSsn(),
            isValidTestDate(),
            isValidTestAnswers(),
            isValidProviderCode(),
            isValidInstructorCode()
    );
    return allPredicates;
}
public Map<Boolean, List<OlccStudent>> getStudentsMap(List<OlccStudent> olccStudentsList) {
    List<Predicate<OlccStudent>> allPredicates = getAllPredicates();
    Predicate<OlccStudent> compositePredicate =  allPredicates.stream()
                             .reduce(w -> true, Predicate::and);
    Map<Boolean, List<OlccStudent>> studentsMap= olccStudentsList
                .stream()
                .collect(Collectors.partitioningBy(compositePredicate));
    return studentsMap;
}
public Map<OlccStudent, String> getInvalidStudentsMap(Map<Boolean, List<OlccStudent>> studentsMap) throws Exception {
    List<OlccStudent> invalidStudentsList = 
            studentsMap.entrySet()
             .stream()
             .filter(p -> p.getKey() == Boolean.FALSE)
             .flatMap(p -> p.getValue().stream())
             .collect(Collectors.toList());
    Function<List<OlccStudent>, Map<OlccStudent, String>> invalidStudentFunction = list ->  {
        Map<OlccStudent, String> invalidStudentsMap = new LinkedHashMap<>();
        list.forEach(student-> {
            String errorMessage = getStudentErrorMessage(student);
            invalidStudentsMap.put(student, errorMessage);  
        });
        return invalidStudentsMap;
    };
    Map<OlccStudent, String> invalidStudentsMap = invalidStudentFunction.apply(invalidStudentsList);
    return invalidStudentsMap;
    return null;
}
private String getStudentErrorMessage(OlccStudent student) {
    String firstName = student.getFirstName();
    String middleInitial = student.getMiddleInitial();
    String lastName = student.getLastName();
    String streetAddress = student.getStreetAddress();
    ....
    StringBuilder errorBuilder = new StringBuilder();
    //The predicate 'negate' method returns a predicate that represents the logical negation or opposite
    if (isValidFirstName().negate().test(student)) {
        String error = "Invalid FirstName: " + firstName;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidMiddleInitial().negate().test(student)) {
        String error = "Invalid Middle Initial: " + middleInitial;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidLastName().negate().test(student)) {
        String error = "Invalid LastName: " + lastName;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidStreetAddress().negate().test(student)) {
        String error = "Invalid StreetAddress: " + streetAddress;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
     ...
    if (errorBuilder.length() > 0) {
        errorBuilder.deleteCharAt(errorBuilder.length() - 1);
    } 
    return errorBuilder.toString().trim();
}
Actually I am confuse with the getStudentErrorMessage() that I am calling from list.forEach(). I know collectors provide you Collectors.joining function. Actually I want to do it in a Predicate manner. Like I created list of all predicates and then use it in the streams. Can I do similar thing with my error messages? Like I create stream from my invalid students List, and using Collectors.toMap() , put my Student as a Key and error message as its value.
Thanks
Edit ----------------
public class OlccStudentPredicate {
    public static Predicate<OlccStudent> isValidTestDate() {
        return p -> isValidDate(p.getTestDate());
    }
    public static Predicate<OlccStudent> isValidDateOfBirth() {
        return p -> isValidDate(p.getDateOfBirth());
    }
    ...
    private static boolean isValidDate(String date) {
    boolean valid = false;
    if (StringUtils.isNotBlank(date)) {
        try {
            LocalDate.parse(date, DateTimeFormatter.ofPattern("MM/dd/yyyy"));
            valid = true;
        } catch (DateTimeParseException e) {
        }
    } 
    return valid;
}
@Test
public void test() {
    List<OlccStudent> olccStudentsList = getOlccStudentsList();
    try {
        Map<OlccStudent, String> map = getInvalidStudentsMap(olccStudentsList);
        System.out.println();
        //olccStudentService.getStudentsMap(olccStudentsList);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
private List<OlccStudent> getOlccStudentsList() {
    List<OlccStudent> olccStudentsList = new ArrayList<>();
    OlccStudent student1 = new OlccStudent(1L, 1L, "firstName1", "middleInitial1", "lastName1", "email1", 
            "streetAdress1", "city1", "state1", "1234", "01/22/2015", "phoneNumber1", "01/22/2015", 
            "123456789", "testForm1", "providerCode1", "instructorCode1", "surveyAnswer1", 
            "testIdentifier1",  "testAnswers1");
    OlccStudent student2 = new OlccStudent(2L, 2L, "firstName2", "middleInitial2", "lastName2", "email2", 
            "streetAdress2", "city2", "state2", "5678", "02/22/2015", "phoneNumber2", "02/22/2015", 
            "987654321", "testForm2", "providerCode2", "instructorCode2", "surveyAnswer2", 
            "testIdentifier2",  "testAnswers2");
    OlccStudent student3 = new OlccStudent(3L,3L, "firstName3", "middleInitial3", "lastName3", "email3", 
            "streetAdress3", "city3", "state3", "zip3", "testDate3", "phoneNumber3", "dateOfBirth3", 
            "socialSecurityNumber3", "testForm3", "providerCode3", "instructorCode3", "surveyAnswer3", 
            "testIdentifier3",  "testAnswers3");
    OlccStudent student4 = new OlccStudent(4L, 4L, "firstName4", "middleInitial4", "lastName4", "email4", 
            "streetAdress4", "city4", "state4", "zip4", "testDate4", "phoneNumber4", "dateOfBirth4", 
            "socialSecurityNumber4", "testForm4", "providerCode4", "instructorCode4", "surveyAnswer4", 
            "testIdentifier4",  "testAnswers4");
    olccStudentsList.add(student1);
    olccStudentsList.add(student2);
    olccStudentsList.add(student3);
    olccStudentsList.add(student4);
    return olccStudentsList;
}
private String validate(OlccStudent student) {
    String firstName = student.getFirstName();
    String middleInitial = student.getMiddleInitial();
    String lastName = student.getLastName();
    String streetAddress = student.getStreetAddress();
    String city = student.getCity();
    String state = student.getState();
    String zip = student.getZip();
    String dateOfBirth = student.getDateOfBirth();
    String ssn = student.getSocialSecurityNumber();
    String phoneNumber = student.getPhoneNumber();
    String testDate = student.getTestDate();
    String testForm = student.getTestForm();
    String testAnswers = student.getTestAnswers();
    String providerCode = student.getProviderCode();
    String instructorCode = student.getInstructorCode();
    StringBuilder errorBuilder = new StringBuilder();
    //The predicate 'negate' method returns a predicate that represents the logical negation or opposite
    if (isValidFirstName().negate().test(student)) {
        String error = "Invalid FirstName: " + firstName;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidMiddleInitial().negate().test(student)) {
        String error = "Invalid Middle Initial: " + middleInitial;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidLastName().negate().test(student)) {
        String error = "Invalid LastName: " + lastName;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidStreetAddress().negate().test(student)) {
        String error = "Invalid StreetAddress: " + streetAddress;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidCity().negate().test(student)) {
        String error = "Invalid City: " + city;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidState().negate().test(student)) {
        String error = "Invalid State: " + state;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidZip().negate().test(student)) {
        String error = "Invalid Zip: " + zip;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidDateOfBirth().negate().test(student)) {
        String error = "Invalid DateOfBirth: " + dateOfBirth;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidSsn().negate().test(student)) {
        String error = "Invalid SSN: " + ssn;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidTestDate().negate().test(student)) {
        String error = "Invalid TestDate: " + testDate;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidTestAnswers().negate().test(student)) {
        String error = "Invalid TestAnswers: " + testAnswers;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidProviderCode().negate().test(student)) {
        String error = "Invalid ProvideCode: " + providerCode;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (isValidInstructorCode().negate().test(student)) {
        String error = "Invalid InstructorCode: " + instructorCode;
        errorBuilder.append(error + ERROR_MESSAGE_SEPERATOR);
    }
    if (errorBuilder.length() > 0) {
        errorBuilder.deleteCharAt(errorBuilder.length() - 1);
    } 
    return errorBuilder.toString().trim();
}
public Map<OlccStudent, String> getInvalidStudentsMap(List<OlccStudent> studentsList) throws Exception {
    Map<OlccStudent, String> map = studentsList.stream()
      // Step 1: Validate each student, keeping a track of any error message generated.
      .collect(Collectors.toMap(Function.identity(), student -> validate(student)))
      // Step 2: Keep only those that have an error message associated.
      .entrySet()
      .stream()
      .filter(entry -> entry.getValue() != null)
      // Step 3: Generate a Map.
      .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));
    return map;
}
				
                        
I would create a
ValidationRuleclass to stick together validation predicate and error message formatter:Now
getAllRuleswill look like this:And you can get the map of invalid students in the following way: