change a value from the List using java8 - java

I want a value get nullified before sending to UI.
public class MedicalInfoVO {
private Integer medicalDeoId;
private List<MedicalCoverageVO> medicalCoverages;
private List<MedicalSignsSymptomVO> medicalSignsSymptoms;
}
public class MedicalSignsSymptomVO {
private Integer medicalSignsSymptomId;
private Integer symptomId;
private String symptomType;
private Integer medicalInfoId;
private Integer diagnosisId;
private String diagnosisValue;
}
I need the medicalSignsSymptomId and medicalInfoId get be nullified before send back to the Service call.
medicalInfoVO = retrieveMedicalInfoDetails(authorizationId, claimId);
if(null ==medicalInfoVO || null == medicalInfoVO.getMedicalDeoId()){
medicalInfoVO = retrieveMedicalInfoDetails(authorizationId, null);
medicalInfoVO.setClaimId(claimId);
medicalInfoVO.setMedicalDeoId(null);
if(CollectionUtils.isNotEmpty(medicalInfoVO.getMedicalSignsSymptoms())) {
List<Integer> medicalSignsSymptomIdList = medicalInfoVO.getMedicalSignsSymptoms().stream()
.map(map -> map.getMedicalSignsSymptomId()).collect(Collectors.toList());
medicalSignsSymptomIdList.clear();
List<Integer> medicalInfoIdList = medicalInfoVO.getMedicalSignsSymptoms().stream()
.map(map -> map.getMedicalInfoId()).collect(Collectors.toList());
medicalInfoIdList.clear();
}
}
When I do it with stream functionality the object value is still not getting cleared.
Suppose the obejct MedicalSignsSymptomVO have medicalSignsSymptomId as 3,
I need assign null to it. same for medicalInfoId as well.
Can I know how this can be done using stream functionality, instead of using the foreach functionality.

Not sure what you want to do, but basically the following is "streamy:"
medicalInfoVO.getMedicalSignsSymptoms().forEach(mSSymptoms -> {
mSSymptoms.setMedicalSignsSymptomId(null);
mSSymptoms.setMedicalInfoId(null);
});
One could combine it as follows:
Set<Integer> medicalSignsSymptomIdList = medicalInfoVO.getMedicalSignsSymptoms().stream()
.map(mSSymptoms -> {
Integer id = mSSymptoms.getMedicalSignsSymptomId();
mSSymptoms.setMedicalSignsSymptomId(null);
mSSymptoms.setMedicalInfoId(null);
return id;
})
.collect(Collectors.toSet());
I used a set here, which seems more logical.
Not necessarily good style, to combine too many things in a stream, for better maintenance. Here: changing elements and return a new list.

If you are sending objects to UI, you might not need to set values to null manually.
If you are using Jackson, you can simply put JSONIgnore to ignore fields when object is being serialized/deserialized.
e.g.
#JsonIgnoreProperties(ignoreUnknown = true)
public class MedicalSignsSymptomVO {
#JSONIgnore
private Integer medicalSignsSymptomId;
private Integer symptomId;
private String symptomType;
private Integer medicalInfoId;
private Integer diagnosisId;
private String diagnosisValue;
}
In case you need those fields serialized else where then you can use forEach method as described by #Joop Eggen's answer.

Related

How to update an object without vanishing existing values in java?

I receive an XML in the API request and it will be converted to an Object and it will be saved into database (MSSQL). I may receive an update of the same Object with some added fields.
In this case how to update all the attributes that I receive in the new request without vanishing the existing value. Currently, I am manually comparing the old and new object and setting up the new value using setters.
Is there a library or some better methods that I can make use of to handle this without pain?
Ex:
Class Person {
#Id
private Integer id;
private String firstName;
private String lastName;
private Date tStamp;
private String phone;
private Address address;
private List<EmploymentHistory> job;
}
Assume if I get firstName and lastName in the first request and phone and address and Job in the subsequent request.
currently I am doing something like below. it is depicted for one of the attributes in the object graph. but actual object graph can go to many level.
if(personOld.getlastName() == null && personNew.getlastName != null)
personNew.setlastName("XX");
Any help on this would be really appreciated. TIA!
You may be looking to use Reflect. Reflect allows you to iterate thought methods and attributes of an object. This way you can create a function that does what you want for any object you want.
Also, in Spring you can extend BeanUtils to achive that:
public class ObjectUtils extends BeanUtils {
public static String[] getNullPropertyNames (Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<String>();
for(java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
String[] result = new String[emptyNames.size()];
return emptyNames.toArray(result);
}
public static void copyProperties(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
}
The function copyProperties copies all the new values from src into target. For example if I have 2 objects:
target = { a: null, b: 1, c: 2}
src = { a: 2, b: null, c: 3}
The result would be:
target = { a:2, b: 1, c: 3}

Iterate and invoke a list of methods

Let say I have 2 classes:
public class Person
{
private String name;
private int age;
private Contact contact;
//getter & setter
}
public class Contact
{
private String phone;
private String email;
//getter & setter
}
With the classes above, I want to create 2 instances of Person class, with different field value. Then I want to compare some fields of 2 objects with their getter function, but I don't want to compare all fields.
For example, I want to compare the field name and phone, then I will store this 2 getter method to a list like something below:
List<WhatShouldBeTheDataType> funcList = new ArrayList<>();
funcList.add(MyClass::getName);
funcList.add(MyClass::getContact::getPhone) //I know this won't work, what should be the solution?
then loop through the funcList, pass the 2 objects I want to compare into the function, if the value not same, write something into the database. This can be easily done with ordinary if...else... way, but is it possible to do in Java 8 way?
Below is what I want to achieve in if...else... way:
if(person1.getName() != person2.getName())
{
//message format basically is: "fieldName + value of object 1 + value of object 2"
log.append("Name is different: " + person1.getName() + ", " + person2.getName());
}
if(person1.getContact.getPhone() != person2.getContact().getPhone())
{
log.append("Phone is different: " + person1.getContact.getPhone() + ", " + person2.getContact.getPhone());
}
//other if to compare other fields
It looks like Person and MyClass refer to the same thing in your question.
You need a Function<Person,String>, since your functions accept a Person instance and return a String:
List<Function<Person,String>> funcList = new ArrayList<>();
funcList.add(Person::getName);
funcList.add(p -> p.getContact().getPhone());
For the second function, you can't use a method reference, but you can use a lambda expression instead.
Given an instance of Person, you can apply your functions as follows:
Person instance = ...;
for (Function<Person,String> func : funcList) {
String value = func.apply(instance);
}
to complete Eran's code:
boolean isEqual(Person person1, Person person2){
for (Function<Person,String> function:functionList) {
if (!function.apply(person1).equals(function.apply(person2))) return false;
}
return true;
}
then use the returned boolean to check and update your database.
Although you can use a list of functions (as suggested in Eran's answer), using comparators directly is probably more appropriate for your use case.
You can alternatively use a chain of comparators, and then use the result of compare:
Comparator<Person> comparators = Comparator.comparing((Person p) -> p.getName())
.thenComparing((Person p) -> p.getContact().getPhone());
Person p1 = null, p2 = null;
if(0 != comparators.compare(person1, person2)) {
//p1 and p2 are different
}
Even simpler (and more natural, in my opinion), is overriding equals in Person, and checking if(!person1.equals(person2))
Edit (after update of the question):
Here's a version built on a function list, dynamically generating the log content by adding a field name list:
List<Function<Person, String>> functions =
Arrays.asList(Person::getName, p -> p.getContact().getPhone());
List<String> fieldNames = Arrays.asList("Name", "Phone");
IntStream.range(0, functions.size())
.filter(i -> functions.get(i).apply(person1)
.compareTo(functions.get(i).apply(person2)) != 0)
.mapToObj(i -> String.format("%s is different: %s, %s",
fieldNames.get(i),
functions.get(i).apply(person1),
functions.get(i).apply(person2)))
.forEach(log::append);
This rather takes advantage of the fact that String is already comparable, and avoids creating comparators altogether.

local variable is not known within for loop in lambda java 8 [duplicate]

Modifying a local variable in forEach gives a compile error:
Normal
int ordinal = 0;
for (Example s : list) {
s.setOrdinal(ordinal);
ordinal++;
}
With Lambda
int ordinal = 0;
list.forEach(s -> {
s.setOrdinal(ordinal);
ordinal++;
});
Any idea how to resolve this?
Use a wrapper
Any kind of wrapper is good.
With Java 10+, use this construct as it's very easy to setup:
var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
s.setOrdinal(wrapper.ordinal++);
});
With Java 8+, use either an AtomicInteger:
AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
s.setOrdinal(ordinal.getAndIncrement());
});
... or an array:
int[] ordinal = { 0 };
list.forEach(s -> {
s.setOrdinal(ordinal[0]++);
});
Note: be very careful if you use a parallel stream. You might not end up with the expected result. Other solutions like Stuart's might be more adapted for those cases.
For types other than int
Of course, this is still valid for types other than int.
For instance, with Java 10+:
var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
wrapper.value += "blah";
});
Or if you're stuck with Java 8 or 9, use the same kind of construct as we did above, but with an AtomicReference...
AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
value.set(value.get() + s);
});
... or an array:
String[] value = { "" };
list.forEach(s-> {
value[0] += s;
});
This is fairly close to an XY problem. That is, the question being asked is essentially how to mutate a captured local variable from a lambda. But the actual task at hand is how to number the elements of a list.
In my experience, upward of 80% of the time there is a question of how to mutate a captured local from within a lambda, there's a better way to proceed. Usually this involves reduction, but in this case the technique of running a stream over the list indexes applies well:
IntStream.range(0, list.size())
.forEach(i -> list.get(i).setOrdinal(i));
If you only need to pass the value from the outside into the lambda, and not get it out, you can do it with a regular anonymous class instead of a lambda:
list.forEach(new Consumer<Example>() {
int ordinal = 0;
public void accept(Example s) {
s.setOrdinal(ordinal);
ordinal++;
}
});
As the used variables from outside the lamda have to be (implicitly) final, you have to use something like AtomicInteger or write your own data structure.
See
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables.
An alternative to AtomicInteger is to use an array (or any other object able to store a value):
final int ordinal[] = new int[] { 0 };
list.forEach ( s -> s.setOrdinal ( ordinal[ 0 ]++ ) );
But see the Stuart's answer: there might be a better way to deal with your case.
Yes, you can modify local variables from inside lambdas (in the way shown by the other answers), but you should not do it. Lambdas have been made for functional style of programming and this means: No side effects. What you want to do is considered bad style. It is also dangerous in case of parallel streams.
You should either find a solution without side effects or use a traditional for loop.
If you are on Java 10, you can use var for that:
var ordinal = new Object() { int value; };
list.forEach(s -> {
s.setOrdinal(ordinal.value);
ordinal.value++;
});
You can wrap it up to workaround the compiler but please remember that side effects in lambdas are discouraged.
To quote the javadoc
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement
A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care
I had a slightly different problem. Instead of incrementing a local variable in the forEach, I needed to assign an object to the local variable.
I solved this by defining a private inner domain class that wraps both the list I want to iterate over (countryList) and the output I hope to get from that list (foundCountry). Then using Java 8 "forEach", I iterate over the list field, and when the object I want is found, I assign that object to the output field. So this assigns a value to a field of the local variable, not changing the local variable itself. I believe that since the local variable itself is not changed, the compiler doesn't complain. I can then use the value that I captured in the output field, outside of the list.
Domain Object:
public class Country {
private int id;
private String countryName;
public Country(int id, String countryName){
this.id = id;
this.countryName = countryName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Wrapper object:
private class CountryFound{
private final List<Country> countryList;
private Country foundCountry;
public CountryFound(List<Country> countryList, Country foundCountry){
this.countryList = countryList;
this.foundCountry = foundCountry;
}
public List<Country> getCountryList() {
return countryList;
}
public void setCountryList(List<Country> countryList) {
this.countryList = countryList;
}
public Country getFoundCountry() {
return foundCountry;
}
public void setFoundCountry(Country foundCountry) {
this.foundCountry = foundCountry;
}
}
Iterate operation:
int id = 5;
CountryFound countryFound = new CountryFound(countryList, null);
countryFound.getCountryList().forEach(c -> {
if(c.getId() == id){
countryFound.setFoundCountry(c);
}
});
System.out.println("Country found: " + countryFound.getFoundCountry().getCountryName());
You could remove the wrapper class method "setCountryList()" and make the field "countryList" final, but I did not get compilation errors leaving these details as-is.
To have a more general solution, you can write a generic Wrapper class:
public static class Wrapper<T> {
public T obj;
public Wrapper(T obj) { this.obj = obj; }
}
...
Wrapper<Integer> w = new Wrapper<>(0);
this.forEach(s -> {
s.setOrdinal(w.obj);
w.obj++;
});
(this is a variant of the solution given by Almir Campos).
In the specific case this is not a good solution, as Integer is worse than int for your purpose, anyway this solution is more general I think.

Filter list according to given Object's fields (which could be null)

Is there any sophisticated way to filter through a list of objects when I have a FilterObject..
public class FilterListOfBets {
private final String userId;
private final long betId;
private final int minBetTime;
private final int maxBetTime;
// constructor and get methods ..
}
.. and a list of bets containing all of the fields, so ..
public class Bet {
private final long betId;
private final String userId;
private final long betTime;
}
.. the thing is that some (if not all) of FilterListObject's fields may be set to null. The result depends on the fields that have been set.
In the end I want to have a List filteredList which contains all the Objects that match the criteria.
If I, for instance, knew which values are set, I'd just loop through the list:
for (Bet bet : listOfBets) {
if (betFilter.getUserId == bet.getUserId && betFilter.getBetId == bet.getBetId && ...)
}
if minBetTime and maxBetTime in FilterListOfBets would be set, then the outcome list should also satisfy : minTime < time < maxTime.
You can create an object that you want to compare against and then use something like guava fluentiterable or java stream
FluentIterable.from(listOfObject)
.filter(new Predicate<FilterObject>(FilterObject item) {
return item.equals(myTestItem);
})
.toList();
I have written this on my phone so havnt checked it but hopefully you get the idea

Presto Custom UDF

I've created a custom udf that is registered but when I try to select custom_udf(10) I get the following error:
Exact implementation of BasicPlatform do not match expected java types
Here is my udf, I can't seem to figure out what is wrong with it:
public class ScalarUdfs {
private ScalarUdfs() {};
#ScalarFunction("basic_platform")
#SqlType(StandardTypes.VARCHAR)
public static Slice BasicPlatform(#SqlNullable #SqlType(StandardTypes.INTEGER) Integer id) {
final Slice IOS = Slices.utf8Slice("iOS");
final Slice ANDROID = Slices.utf8Slice("Android");
final Slice WEB = Slices.utf8Slice("Web");
final Slice OTHER = Slices.utf8Slice("Other");
final Map<Integer, Slice> PLATFORM_MAP = new HashMap<Integer, Slice>() {{
put(20, IOS);
put(42, ANDROID);
put(100, WEB);
}};
if (id == null || !PLATFORM_MAP.containsKey(id)) {
return OTHER;
}
return PLATFORM_MAP.get(id);
}
}
Does anything seem obviously wrong? I want it to return a string given an int as a parameter, and I think the java and sql types match (Integer -> Integer), (Slice -> varchar).
Thanks
The question was also asked and answered on presto-users:
You have to use #SqlNullable #SqlType(StandardTypes.INTEGER) Long id (as SQL integer is backed by Long in Java).

Categories