Convert iterator code to Stream - java

converting this nested iteration is blowing my mind could someone convert this to a parallel stream? I want to be able to check for assignments that match my criteria for each of my account team members. I've just switched to java 8 and am struggling to wrap my head around the best way to leverage parallel streams to preform this aggregation.
for(Iterator<BasicDBObject> iterator = members.iterator(); iterator.hasNext();) {
BasicDBObject member = iterator.next();
//if the member doesn't have a valid assignment remove them.
boolean memberContainsValidAssignment = false;
BasicDBObject role = member.get("accountTeamRole") == null ? null : (BasicDBObject) member.get("accountTeamRole");
if (accTeamRoleCodes != null && !accTeamRoleCodes.contains(role.get("code"))) {
iterator.remove();
continue;
}
List<BasicDBObject> assignments = member.get("assignments") == null ? new ArrayList<>() : (List) member.get("assignments");
for (Iterator<BasicDBObject> assignIterator = assignments.iterator(); assignIterator.hasNext(); ) {
BasicDBObject assignment = assignIterator.next();
Date endDate = (Date) assignment.get("assignmentValidToDate");
Date startDate = (Date) assignment.get("assignmentValidFromDate");
if(startDate == null){//this is junk, should have never been allowed.
LOGGER.warn("There's no start date for this assignment. {}", assignment.toString());
assignIterator.remove();
continue;
}
//1. open ended assignment, it lives on forever. 2.falls in between active date.
else if(endDate == null ||(activeDate.after(startDate) && activeDate.before(endDate))){
LOGGER.debug("adding an assignment. {}", assignment.toString());
memberContainsValidAssignment = true;
convertDatesToString(assignment);
continue;
}
}
if(!memberContainsValidAssignment){
iterator.remove();
}
}

Here's a shot at converting your code to use streams. Please, check the details of the filter conditions.
List<BasicDBObject> invalidAssignments = members.stream()
.filter(member -> accTeamRoleCodes == null ||
member.get("accountTeamRole") == null ||
accTeamRoleCodes.contains(member.get("accountTeamRole").get("code"))
)
.flatMap(member -> member.get("assignments").stream())
.filter(assignment -> (Date) assignment.get("assignmentValidFromDate") != null &&
((Date) assignment.get("assignmentValidToDate") != null ||
(activeDate.after((Date)assignment.get("assignmentValidFromDate")) &&
activeDate.before((Date)assignment.get("assignmentValidToDate"))))
)
.collect(Collectors.toList());
To make things parallel would just be a matter of using members.parallelStream instead of members.stream.

Related

Record validation to identify error records by string comparison

I am trying to read avro records from a topic and validate it before loading it into a table.
It's a normal validation, based on below scenario :
There are two fields in the schema, VP and AH.
Condition 1:
VP and AH can't have A value at the same time, set err_val ='E' in this case.
Condition 2:
VP and AH must have value A or I, set err_val = 'E' in case of any other value.
So, my condition is like this :
// logic to identify error records
if ((record.value().get("AH").toString().equals("A") && record.value().get("VP").toString().equals("A"))
|| ((!"I".equals(record.value().get("AH").toString())) || (!"A".equals(record.value().get("AH").toString())))
|| ((!"I".equals(record.value().get("VP").toString())) || (!"A".equals(record.value().get("VP").toString())))
)
{
id = record.value().get("ID").toString();
id_type = record.value().get("ID_TYPE").toString();
ah = record.value().get("AH").toString();
vp = record.value().get("VP").toString();
effective_date = record.value().get("EFFECTIVE_DATE").toString();
end_date = record.value().get("END_DATE").toString();
err_val = "E";
} else {
id = record.value().get("ID").toString();
id_type = record.value().get("ID_TYPE").toString();
ah = record.value().get("AH").toString();
vp = record.value().get("VP").toString();
effective_date = record.value().get("EFFECTIVE_DATE").toString();
end_date = record.value().get("END_DATE").toString();
err_val = "";
}
The condition 1 is handled by first line inside if statement and it's working as expected.
But, the next two lines are not working for condition 2.
Even if I am producing record like this :
value={"ID": "461761581", "ID_TYPE": "IN", "AH": "I", "VP": "A", "EFFECTIVE_DATE": "20200501", "END_DATE": "20991231"}
I am getting 'E' in the output, which is not correct.
461761581,IN,I,A,20200501,20991231,E
Please suggest.
Try this:
String AH = record.value().get("AH").toString();
String VP = record.value().get("VP").toString();
Set<String> filter = Set.of("A", "I");
if (("A".equals(AH) && "A".equals(VP)) || (!filter.contains(AH) && !filter.contains(VP))) {
//do something
} else {
//do something
}
You can also use String.matches()
if (("A".equals(AH) && "A".equals(VP)) || (!AH.matches("A|I") && !VP.matches("A|I"))) {
//do something
} else {
//do something
}

Avoiding a NullPointerException when the user enters invalid data

How can I avoid a NullPointerException here if the user passes a null value for a date? Ideally, I would use a simple if statement.
SimpleDateFormat dateFormatter = new SimpleDateFormat("MM/dd/yyyy");
SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
String layupDate = dateFormatter.format(orderLayupDateSet.getLayupDate());
String layupTime = timeFormatter.format(orderLayupDateSet.getLayupDate());
if(layupDate == null) {
}
You validate the date (orderLayupDateSet.getLayupDate()) in a liner (just creating a new date if null here but you can do as you please);
(orderLayupDateSet.getLayupDate() == null) ? new Date() : orderLayupDateSet.getLayupDate()
You can also go the traditional
if (orderLayupDateSet.getLayupDate() == null) {
orderLayupDateSet.setLayupDate(new Date());
}
Or you can do it the other way around with a utility, and calling the utility function to check for you - seeing that you return a String in your example above. Like the below;
public class DateUtils {
public static String formatDateTime(Date dateOrNull) {
return (dateOrNull == null ? null : DateFormat.getDateTimeInstance().format(dateOrNull));
}
}
Which in your code can be
String layupDate = (orderLayupDateSet == null || orderLayupDateSet.getLayupDate() ? null : dateFormatter.format(orderLayupDateSet.getLayupDate()));

Java String appending with comma as a separator

I have a requirement where I need to append multiple values from multiple web service calls into one final string with comma as a separator.
Some of the values might be null, in that case I need to check for not null and then append it as empty string.
If there is no value for one of the string, comma should not get appended.
Please help me resolving this. here is the code what I did.
StringBuilder sb = new StringBuilder();
if (usersList.totalCount != 0 && usersList.totalCount >= 1) {
logger.info("usersList.totalCount ----->"
+ usersList.totalCount);
for (KalturaUser user : usersList.objects) {
if (user.id != null) {
sb.append(userId);
}
if (user.firstName != null) {
sb.append(",").append(userFirstName);
}
if (user.lastName != null) {
sb.append(",").append(user.lastName);
}
if (user.email != null) {
sb.append(",").append(user.email);
}
if (user.roleNames != null) {
sb.append(",").append(user.roleNames);
}
if (user.partnerData != null) {
sb.append(",").append(user.partnerData);
}
}
System.out.println(sb);
}
Thanks,
Raji
I think you are looking for something like this:
public static String asString(Object value) {
return value == null ? "" : value.toString();
}
for (KalturaUser user : usersList.objects) {
sb.append(asString(user.id));
sb.append(",").append(asString(user.firstName));
sb.append(",").append(asString(user.lastName));
sb.append(",").append(asString(user.email));
sb.append(",").append(asString(user.roleNames));
sb.append(",").append(asString(user.partnerData));
}
Well, in your tests like
if (user.id != null) {
sb.append(userId);
}
you are checking user.id but appending userId. These are two different variables.
You should probably change it into
if (user.id != null) {
sb.append(user.id); //instead of sb.append(userId);
}
It is not clear what your problem is, but if you are looking for a better or different approach, I found that it is best to append to a List<String> and then use StringUtils.join to produce the final string.
You can use a class from a google library called Joiner.
String concatenedString = Joiner.on(",").skipNulls().join(itemToAdd);
You can find this class on google-collections-1.0.jar
I would do something like that. It's based on Java8 streams, but here you don't need to do the non-null check on every property(.filter does this for you). I assumed that users.object is an array list, if it's not you might need to convert it into stream in other way
if (userList.totalCount > 0) {
logger.info("usersList.totalCount ----->" + usersList.totalCount);
String result = userList
.objects
.stream()
.map(user -> {
return Stream.of(user.firstName, user.id, user.lastName, user.email, user.roleNames, user.partnerData) //get the properties you need into the stream
.filter(property -> property != null) // filter out null properties
.collect(Collector.joining(",")); //join them by comma
})
.collect(Collector.joining(",")); //join user strings with comma
System.out.println(result);
}

How to traverse an ArrayList of Objects

I have an ArrayList of custom made Users. The list is already sorted by manager.My goal is to go through the list by manager and add each user to the body of an email depending on their expiration date.
A User is basically built like this from the database. All necessary accessors/mutators are present:
id|fName|lName|...|manager
Go through the users and notify the manager if the user is expiring:
To: Manager
Expiring in 10 days
<User>
<User>
Expiring in 30 days
<User>
StringBuilder body = new StringBuilder();
ArrayList<Users> contractors;
Date today = cal.getTime();
...
if(contractors != null && contractors.size() > 0){
for(int i = 0; i < contractors.size(); i++){
if(i+1 > contractors.size()){
//do something to avoid outOfBounds and still access last item in the list
}else{
if (contractors.get(i+1).getManager() != null){
if(manager.equals(contractors.get(i+1).getManager())){
if(today.compareTo(contractor.getExpiration()){
//build body of email
}
}
}
sendEmail(manager, body.toString());
}else{
//new manager
body.equals(""); // reset email for next run
}
}
After the email is sent I want to move on to the next set of users based on manager. My problem is that I'm having trouble with the logic behind traversing the array by manager and then resetting everytime for each new manager. I'm thinking that I need another for loop?
What's the best way to do this? thanks
Edit
When implemented this way:
I would do it something like this:
if (contractors != null) {
String currentManager = null;
for (User contractor : contractors) {
String contractorManager = contractor.getManager();
if (!contractorManager.equals(currentManager) {
// a new manager
if (currentManager != null) {
sendEmail(body.toString());
body.setLength(0);
}
currentManager = contractorManager;
}
//build body of email ...
}
// send the email for the last manager
sendEmail(body.toString());
}
Iterate the list of Users and add them to a Map keyed by manager with a Set of employees per manager. Something like,
if (contractors != null && contractors.size() > 0) {
Map<String, Set<Users>> map = new HashMap<>();
for (Users contractor : contractors) {
String manager = contractor.getManager();
if (manager == null) {
manager = contractor.getName();
}
Set<Users> employees = map.get(manager);
if (employees == null) {
employees = new HashSet<>();
map.put(manager, employees);
}
employees.add(contractor);
} // now you can iterate the keySet and then each manager's employees like
for (String manager : map.keySet()) {
Set<Users> employees = map.get(manager);
for (Users u : employees) {
// ...
}
}
}
You should go for a "foreach" loop on contractors, more info here
another example here
If they are already sorted, you can take this approach (I'm using "ManagerObject" to represent the return type of Users.getManager() - replace with the actual class name):
StringBuilder body = new StringBuilder();
ManagerObject currentManager = null;
for (Users contractor : contractors) {
if (currentManager != null && !(contractor.getManager().equals(currentManager)) {
sendEmail(body.toString());
body.equals("");
}
currentManager = contractor.getManager();
// Add stuff to the body for this contractor
}
If the call to Users.getManager() is computationally expensive for some reason, this can be rejiggered to only set the currentManager upon a change in value

TreeMap<DateTime, Object> is not sorting

I have TreeMap using the Joda DateTime object and is does not seem to be sorting here is the definition:
TreeMap<DateTime, HolderAnswer> dateTimeTreeMap = new TreeMap<DateTime, HolderAnswer>();
I added in the values as follows (I'm just using a generic sql statement here);
//then get previously selected answers to move to the top of the list
String sql = "Select ActionDT, RecID, TextID, Text, Value from Foo";
Cursor c = DataBaseConnector.query(sql);
if (c != null) {
if (c.moveToFirst()) {
do {
HolderAnswer answer = null;
boolean valueAlreadyIn = false;
DateTime dt = formatter.parseDateTime(c.getString(c.getColumnIndex("ActionDT")));
//we will be adding in the options in the next section, setting to null for now.
answer = new HolderAnswer(c.getInt(c.getColumnIndex("RecID")),c.getInt(c.getColumnIndex("TextID")),null,count,c.getString(c.getColumnIndex("Text")));
//////////////////////////////////////////////////////////////
Iterator<Entry<DateTime, HolderAnswer>> it = dateTimeTreeMap.entrySet().iterator();
while (it.hasNext()) {
Entry<DateTime, HolderAnswer> pairs = it.next();
HolderAnswer tempAnswer = (HolderAnswer) pairs.getValue();
DateTime tempDateTime = (DateTime) pairs.getKey();
//if answers match, transfer over options
if (answer.getTextID() == tempAnswer.getTextID()) {
valueAlreadyIn = true;
}
}
if (!valueAlreadyIn) {
dateTimeTreeMap.put(dt,answer);
}
//////////////////////////////////////////////////////////////////
//count++;
} while(c.moveToNext());
c.close();
c = null;
}
}
When I print out the values, they don't seem to be sorted, they come out in no discernable pattern. Even doing:
dateTimeTreeMap.descendingMap();
Does nothing. Am I missing something?
The descendingMap() method is used to return a reverse order view of the mappings contained in this map so it looks like you're forgetting to assign the sorted map to the original one.
dateTimeTreeMap = dateTimeTreeMap.descendingMap();

Categories