How to get parent POJO class field value using streams - java-8

I have two classes - Student and StudentDetails.
My goal is to get data in the form of Map <studentName,Map<subjectName, subjectNo>>.
When using streams, not able to fetch data of parent class(Student).
public class Student {
private String studentName;
private Map<String, Long> studentDetails;
public Map<String, Long> getStudentDetails() {
return studentDetails;
}
public String getStudentName() {
return studentName;
}
}
class StudentDetails {
private String subjectName;
private String subjectNo;
public String getFSubjectName() {
return subjectName;
}
public String getSubjectNo() {
return subjectNo;
}
}
public class Main {
public static void main(String[] args) {
Collection<Student> student = null;
Map<String, Map<String, Long>> sts = student.stream()
.map(st -> st.getStudentDetails().values())
.flatMap(st -> st.stream())
.collect(Collectors.groupingBy(Student::getStudentName,
Collectors.toMap(StudentDetails :: getFatherName, StudentDetails:: getRollNo )));
}
}
Error : The type Student does not define getStudentName(T) that is applicable here.

You "lost" your Student while doing:
.map(st -> st.getStudentDetails().values())
.flatMap(st -> st.stream()) // this is a Stream<StudentDetails> now
Thus if you need it just map it:
.flatMap(st -> st.getStudentDetails()
.values()
.stream()
.map(sd -> new SimpleEntry<>(st, sd)))
.collect(Collectors.groupingBy(
en -> en.getKey().getStudentName(),
Collectors.toMap(
en -> en.getValue().getFartherName(),
en -> en.getValue().getRollNo()
)
))
This can be done without Streams too; you could try to look at Map::compute

Related

Create Mono with Map object present within another Mono object

I am new to reactive and not able to get around this.
I have following Dtos:
public class User {
int id;
Map<String, Car> carsMap;
}
public class Car {
String carName;
}
// Response object
public class VehiclesInfo {
List<String> vehicleName;
}
From database I am getting Mono<User> when querying by userId.
And I have to return Mono<VehiclesInfo>.
So, I have to map the carsMap received from Mono<User> into List i.e. List of carName and set that into VehiclesInfo and return that as Mono i.e. Mono<VehiclesInfo>.
I am doing it like below. Please let me know how this can be done without blocking.
// userMono is returned by database query
Mono<User> userMono = getUserInfoById(userId);
Optional<User> userOptional = userMono.blockOptional();
if (userOptional.isPresent()) {
User user1 = userOptional.get();
Flux<Car> carFlux = Flux.fromIterable(user1.getCarsMap().keySet())
.flatMap(i -> {
final Car c = new Car();
c.setCarName(i);
return Mono.just(c);
});
carFlux.subscribe(c -> System.out.println(c.getCarName()));
}

Java stream : convert list of one object to other

I am trying to learn map function in Stream
public class EmployeeInformationTest {
public static void main(String args[]) {
List<Employee> employees = Arrays.asList(
new Employee("Jai"),
new Employee("Adithya"),
new Employee("Raja"));
List<String> names = employees.stream()
.map(s -> s.getEmployeeName()) // Lambda Expression
.collect(Collectors.toList());
System.out.println(names);
}
}
we have above code and somehow it is giving us List of String from List of Employee. Say, we have other class Person in which we have field as name
public class Person {
private String name;
}
so is it feasible via map or some other function in stream so that I can get the List of Person rather than List of String in above code
sure thing, just change the map function to:
.map(s -> new Person(s.getEmployeeName()))
or if there is no such constructor:
.map(s -> {
Person p = new Person();
p.setName(s.getEmployeeName());
return p;
})

Generic Search and Filter by dynamic fields for Criteria (Global Search)

I have a scenario where I need to add Criteria to perform search and filter in Spring using mongoTemplate.
Scenario:
Lets say I have Student, Course and PotentialStudent. and I have to define only certain fields to be used for search and filter purpose. For PotentialStudent, it contains both Student and Course information that is collected before all required information is gathered to be filled to Student and Course.
Search Fields are the fields to be used for searching either of the fields. For example: get values matching in either courseName or courseType in Course.
Filter is to be used to filter specific fields for matching multiple values and the values to be filtered on field is set on FilterParams. Meaning, if I get values in FilterParams.studentType then for PotentialStudent I should
add Criteria to search inside PotentialStudent's student.type for list of values whereas if for Student add Criteria to search in Student's type.
public abstract class Model {
#Id
protected String id;
#CreatedDate
protected Date createdDateTime;
#LastModifiedDate
protected Date modifiedDateTime;
protected abstract List<String> searchFields();
protected abstract Map<String, String> filterFields();
}
#Getter
#Setter
#Document("student")
public class Student extends Model {
private String firstName;
private String lastName;
private String address;
private StudentType type;
#Override
protected List<String> searchFields() {
return Lists.newArrayList("firstName","lastName","address");
}
#Override
protected Map<String, String> filterFields() {
Map<String, String> filterMap = Maps.newHashMap();
filterMap.put("studentType", "type");
return filterMap;
}
}
#Getter
#Setter
#Document("course")
public class Course extends Model {
private String courseName;
private String courseType;
private int duration;
private Difficulty difficulty;
#Override
protected List<String> searchFields() {
return Lists.newArrayList("courseName","courseType");
}
#Override
protected Map<String, String> filterFields() {
Map<String, String> filterMap = Maps.newHashMap();
filterMap.put("courseDifficulty", "difficulty");
return filterMap;
}
}
#Getter
#Setter
#Document("course")
public class PotentialStudent extends Model {
private Student student;
private Course course;
#Override
protected List<String> searchFields() {
return Lists.newArrayList("student.firstName","student.lastName","course.courseName");
}
#Override
protected Map<String, String> filterFields() {
Map<String, String> filterMap = Maps.newHashMap();
filterMap.put("studentType", "student.type");
filterMap.put("courseDifficulty", "course.difficulty");
return filterMap;
}
}
}
public class FilterParams {
private List<StudentType> studentTypes;
private List<Difficulty> difficulties;
}
public class PageData<T extends Model> {
public void setPageRecords(List<T> pageRecords) {
this.pageRecords = pageRecords;
}
private List<T> pageRecords;
}
//Generic Search Filter Implementation Class
public class GenericSearchFilter {
public <T extends Model> PageData getRecordsWithPageSearchFilter(Integer page, Integer size, String sortName, String sortOrder, String value, FilterParams filterParams, Class<T> ormClass) {
PageRequestBuilder pageRequestBuilder = new PageRequestBuilder();
Pageable pageable = pageRequestBuilder.getPageRequest(page, size, sortName, sortOrder);
Query mongoQuery = new Query().with(pageable);
//add Criteria for the domain specified search fields
Criteria searchCriteria = searchCriteria(value, ormClass);
if (searchCriteria != null) {
mongoQuery.addCriteria(searchCriteria);
}
//Handle Filter
query.addCriteria(Criteria.where(filterFields().get("studentType")).in(filterParams.getStudentTypes()));
query.addCriteria(Criteria.where(filterFields().get("courseDifficulty")).in(filterParams.getDifficulty()));
List<T> records = mongoTemplate.find(mongoQuery, ormClass);
PageData pageData = new PageData();
pageData.setPageRecords(records);
return pageData;
}
private <T extends BaseDocument> Criteria searchCriteria(String value, Class<T> ormClass) {
try {
Criteria orCriteria = new Criteria();
if (StringUtils.isNotBlank(value)) {
BaseDocument document = ormClass.getDeclaredConstructor().newInstance();
Method method = ormClass.getDeclaredMethod("searchFields");
List<String> records = (List<String>) method.invoke(document, null);
Criteria[] orCriteriaArray = records.stream().map(s -> Criteria.where(s).regex(value, "i")).toArray(Criteria[]::new);
orCriteria.orOperator(orCriteriaArray);
}
return orCriteria;
} catch (Exception e) {
log.error(e.getMessage());
}
return null;
}
}
Given this scenario, my question is how to handle filter cases in better and dynamic way and how to implement a Global search if needed to search in all Document types for specified fields on each types.

Java 8 collectors groupBy and into a separate class

I have a list of Persons which have some duplicate names.
class Person {
String name;
}
I want to convert it to the list of GroupedPersons which contain the common name and the list of all Persons who have that name.
class GroupedPerson {
String name;
List<A> as;
}
Is it possible to do this with one collector and without any intermediate mapping or extra classes?
I suppose one way would be:
persons
.stream()
.collect(Collectors.collectingAndThen(Collectors.groupingBy(
Person::getName),
map -> {
return map.entrySet()
.stream()
.map(en -> new GroupedPerson(en.getKey(), en.getValue()))
.collect(Collectors.toList());
}));
Or, you could use toMap:
persons
.stream()
.collect(Collectors.toMap(
Person::getName,
x -> {
List<Person> l = new ArrayList<>();
l.add(x);
return new GroupedPerson(x.getName(), l);
},
(left, right) -> {
left.getAs().addAll(right.getAs());
return left;
}))
.values();
Yes, you can do that. Here is a way to do it:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class GroupedStream {
public static void main(String[] args) {
List<GroupedPerson> groupedPersons = Arrays.asList("john harry john harry sam jordon bill steve bill".split(" "))
.stream()
.map(name -> new Person(name))
.map(person -> new Object[] {person.name, new ArrayList<>()})
.collect(Collectors.toMap(tuple-> (String) tuple[0], tuple -> (List<Person>) tuple[1] ))
.entrySet()
.stream()
.map(entry -> new GroupedPerson(entry.getKey(), entry.getValue()))
.collect(Collectors.toList());
}
static class Person {
public final String name;
public Person(String name) {
super();
this.name = name;
}
}
static class GroupedPerson {
public final String name;
public final List<Person> person;
public GroupedPerson(String name, List<Person> person) {
this.name = name;
this.person = person;
}
}
}
if you modify your GroupedPerson model to be something along the lines of:
public class GroupedPerson {
private String name;
private List<Person> people;
public GroupedPerson(String name) {
this.name = name;
people = new ArrayList<>();
}
public void addPeople(List<Person> people) {
this.people.addAll(people);
}
public void addPerson(Person person){
people.add(person);
}
public List<Person> getPeople(){
return Collections.unmodifiableList(people);
}
public String getName() {
return name;
}
}
Then you can have a Collection of GroupedPerson objects with the names and all the corresponding people with that specific name like this:
Collection<GroupedPerson> resultSet = peopleList
.stream()
.collect(Collectors.toMap(Person::getName,
p -> {
GroupedPerson groupedPerson = new GroupedPerson(p.getName());
groupedPerson.addPerson(p);
return groupedPerson;
},
(p, p1) -> {
p.addPeople(p1.getPeople());
return p;
}
)).values();
if for some reason you don't want the receiver type to be Collection<T> then you can convert to a specific collection type if deemed appropriate by simply doing.
List<GroupedPerson> result = new ArrayList<>(resultSet);

Playframework! Using #select for a Enum property

I'm trying to create a #select input for a enum field. Everything works fine until the form is submitted. It fails with a weird validation error -> "error.invalid"
here's my code
Enum class
package model;
...
public enum UserType {
UserType_Admin("Administrator"), UserType_Monitor("Monitor"), UserType_Audit("Audit");
private String desc;
private UserType(String desc) {
this.desc = desc;
}
#Override
public String toString() {
return Messages.get(desc);
}
public String getLabel() {
return toString();
}
public String getKey() {
return super.toString();
}
public static Map<String, String> options() {
LinkedHashMap<String, String> options = new LinkedHashMap<String, String>();
for (UserType ut : UserType.values()) {
Integer o = ut.ordinal();
options.put(o.toString(), ut.desc);
}
return options;
}
}
My Entity
#Entity
public class User extends Model {
...
#Id
public Long userID;
public UserType user_type;
}
Scala template
#form(routes.Users.save(userID)) {
#select(
userForm("user_type"),
options(model.UserType.options),
'_label -> Messages("UserType"), '_default -> Messages("choose_user_type"),
'_showConstraints -> true
)
}
on the controller the Save method:
public static Result save(Long userID) {
Form<User> userForm = form(User.class).bindFromRequest();
if (userForm.hasErrors()) { <- here it says that has errors
return badRequest(useredit.render(new Session(session()), userID,
userForm, new User()));
}
...
}
if I inspect the userForm variable, I get:
Form(of=class model.User, data={user_type=0}, value=None,
errors={user_type=[ValidationError(user_type,error.invalid,[])]})
The field user_type has the correct value, 0 if I choose the first item, 1 for the second, etc.
Screenshot
Anyone has a clue or a workaround for this? Maybe disable validation for this field? Tks guys

Resources