How to apply composite BiPredicate in anyMatch() java 8 streams - java

I have two Lists. One I created from Database and one from Csv file. Now I want to collect records in a list that contain both in database and csv file. I wrote code like below
BiPredicate<Trainee, Trainee> sameTrainee = (dbTrainee, csvTrainee) -> {
String dbTraineeFirstName = dbTrainee.getFirstName();
String dbTraineeLastName = dbTrainee.getLastName();
String dbTraineeEmail = dbTrainee.getEmail();
LocalDateTime dbTraineeCompletionDate = dbTrainee.getSessionDateTime();
String text = dbTraineeCompletionDate.format(DATE_TIME_FORMATTER);
LocalDateTime dbTraineeSessionDateTime = LocalDateTime.parse(text);
String dbTraineePhoneNumber = dbTrainee.getPhoneNumber();
String dbTraineeSsn = dbTrainee.getSocialSecurityLastFour();
String dbTraineeStreetOne = dbTrainee.getStreetOne();
String dbTraineeCity = dbTrainee.getCity();
String csvTraineeFirstName = csvTrainee.getFirstName();
String csvTraineeLastName = csvTrainee.getLastName();
String csvTraineeEmail = csvTrainee.getEmail();
LocalDateTime csvTraineeSessionDateTime = csvTrainee.getSessionDateTime();
String csvTraineePhoneNumber = csvTrainee.getPhoneNumber();
String csvTraineeSsn = csvTrainee.getSocialSecurityLastFour();
String csvTraineeStreetOne = csvTrainee.getStreetOne();
String csvTraineeCity = csvTrainee.getCity();
int dbTraineeSsnLength = dbTraineeSsn.length();
int csvTraineeSsnLength = csvTraineeSsn.length();
if (dbTraineeSsnLength != csvTraineeSsnLength) {
if (dbTraineeSsnLength == 4 && dbTraineeSsn.startsWith("0")) {
String dbTraineeSsnLast3Digits = dbTraineeSsn.substring(dbTraineeSsn.length() - 3);
if (csvTraineeSsnLength == 3 && csvTraineeSsn.endsWith(dbTraineeSsnLast3Digits)) {
csvTraineeSsn = "0" + csvTraineeSsn;
}
}
}
return dbTraineeFirstName.equals(csvTraineeFirstName)
&& dbTraineeLastName.equals(csvTraineeLastName)
&& dbTraineeEmail.equals(csvTraineeEmail)
&& dbTraineeSessionDateTime.equals(csvTraineeSessionDateTime)
&& dbTraineePhoneNumber.equals(csvTraineePhoneNumber)
&& dbTraineeSsn.equals(csvTraineeSsn)
&& dbTraineeStreetOne.equals(csvTraineeStreetOne)
&& dbTraineeCity.equals(csvTraineeCity);
};
and called it like
List<Trainee> foundInBothList = dbMonthlyTraineeList.stream()
.filter(dbTrainee -> csvTraineeList.stream()
.anyMatch(csvTrainee -> {
return sameTrainee.test(dbTrainee, csvTrainee);
})
).collect(Collectors.toList());
List<Trainee> notInFileList = dbMonthlyTraineeList.stream()
.filter(dbTrainee -> csvTraineeList.stream()
.noneMatch(csvTrainee -> {
return sameTrainee.test(dbTrainee, csvTrainee);
})
).collect(Collectors.toList());
It works fine. But as my BiPredicate is getting long and untidy. So I made a class and collect all the predicates in a Collection like below
public class PlcbMonthlyReportStatisticsBiPredicates {
public static BiPredicate<Trainee, Trainee> isValidFirstName() {
return (dbTrainee, csvTrainee) -> {
String dbTraineeFirstName = dbTrainee.getFirstName();
String csvTraineeFirstName = csvTrainee.getFirstName();
return dbTraineeFirstName.equals(csvTraineeFirstName);
};
}
public static BiPredicate<Trainee, Trainee> isValidSsn() {
return (dbTrainee, csvTrainee) -> {
String dbTraineeSsn = dbTrainee.getSocialSecurityLastFour();
String csvTraineeSsn = csvTrainee.getSocialSecurityLastFour();
...
return dbTraineeSsn.equals(csvTraineeSsn);
};
}
....
public static List<BiPredicate<Trainee, Trainee>> getAllBiPredicates() {
List<BiPredicate<Trainee, Trainee>> allPredicates = Arrays.asList(
isValidFirstName(),
isValidSsn(),
...
);
return allPredicates;
}
}
Now I Collect all the predicates but how can I apply these predicates in my anyMatch() and noneMatch(). I tried this but of-cources getting error
List<Trainee> foundInBothList1 = dbMonthlyTraineeList.stream()
.filter(dbTrainee -> csvTraineeList.stream()
.anyMatch(csvTrainee -> {
List<BiPredicate<Trainee, Trainee>> allBiPredicates = getAllBiPredicates();
return allBiPredicates.stream().reduce(BiPredicate::and).orElse((x,y)->true); //error
})
).collect(Collectors.toList());
How can I apply this. Is my approach is right?
**Edit
#Entity
public class Trainee {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
private LocalDateTime sessionDateTime;
private String firstName;
....
#Override
public boolean equals(Object otherObject) {
// Are the same?
if (this == otherObject) {
return true;
}
// Is otherObject a null reference?
if (otherObject == null) {
return false;
}
// Do they belong to the same class?
if (this.getClass() != otherObject.getClass()) {
return false;
}
// Get the reference of otherObject in a otherTrainee variable
Trainee otherTrainee = (Trainee)otherObject;
LocalDateTime dbTraineeCompletionDate = this.getSessionDateTime();
String text = dbTraineeCompletionDate.format(DATE_TIME_FORMATTER);
LocalDateTime dbTraineeSessionDateTime = LocalDateTime.parse(text);
String dbTraineeSsn = this.socialSecurityLastFour;
String csvTraineeSsn = otherTrainee.getSocialSecurityLastFour();
int dbTraineeSsnLength = dbTraineeSsn.length();
int csvTraineeSsnLength = csvTraineeSsn.length();
if (dbTraineeSsnLength != csvTraineeSsnLength) {
if (dbTraineeSsnLength == 4 && dbTraineeSsn.startsWith("0")) {
String dbTraineeSsnLast3Digits = dbTraineeSsn.substring(dbTraineeSsn.length() - 3);
if (csvTraineeSsnLength == 3 && csvTraineeSsn.endsWith(dbTraineeSsnLast3Digits)) {
csvTraineeSsn = "0" + csvTraineeSsn;
}
}
}
boolean isEqual = (this.firstName.equals(otherTrainee.firstName)
&& this.lastName.equals(otherTrainee.lastName)
&& this.email.equals(otherTrainee.email)
&& dbTraineeSessionDateTime.equals(otherTrainee.sessionDateTime)
&& this.phoneNumber.equals(otherTrainee.phoneNumber)
&& dbTraineeSsn.equals(csvTraineeSsn)
&& this.streetOne.equals(otherTrainee.streetOne)
&& this.city.equals(otherTrainee.city)
);
return isEqual;
}
#Override
public int hashCode() {
int hash = 37;
int code = 0;
code = (firstName == null ? 0 : firstName.hashCode());
hash = hash * 59 + code;
code = (lastName == null ? 0 : lastName.hashCode());
hash = hash * 59 + code;
code = (email == null ? 0 : email.hashCode());
hash = hash * 59 + code;
code = (sessionDateTime == null ? 0 : sessionDateTime.hashCode());
hash = hash * 59 + code;
code = (phoneNumber == null ? 0 : phoneNumber.hashCode());
hash = hash * 59 + code;
code = (socialSecurityLastFour == null ? 0 : socialSecurityLastFour.hashCode());
hash = hash * 59 + code;
code = (streetOne == null ? 0 : streetOne.hashCode());
hash = hash * 59 + code;
code = (city == null ? 0 : city.hashCode());
hash = hash * 59 + code;
return hash;
}
}
Edit 2 (After overridng hascode() and equals())
-------------------------------------------------
Found in both:
List<Trainee> foundInBothList1 = dbMonthlyTraineeList.stream()
.filter(dbTrainee -> csvTraineeList.stream()
.anyMatch(csvTrainee -> {
return allBiPredicates.stream().reduce(BiPredicate::and).orElse((x,y)->true).test(dbTrainee, csvTrainee);
})
).collect(Collectors.toList());
List<Trainee> foundInBothList = new ArrayList<>(dbMonthlyTraineeList);
//foundInBothList.retainAll(new HashSet<>(csvTraineeList));
foundInBothList.retainAll(csvTraineeList);
Found in database but not in CSV
List<Trainee> notInCsvFileList1 = dbMonthlyTraineeList.stream()
.filter(dbTrainee -> csvTraineeList.stream()
.noneMatch(csvTrainee -> {
return allBiPredicates.stream().reduce(BiPredicate::and).orElse((x,y)->true).test(dbTrainee, csvTrainee);
})
).collect(Collectors.toList());
//find out that elements of dbMonthlyTraineeList which is not present in arraylist(csvTraineeList).
List<Trainee> notInCsvFileList = new ArrayList<>(dbMonthlyTraineeList);
notInCsvFileList.removeAll(csvTraineeList);

It looks like you are over-thinking this. Why not just override equals with your sameTrainee bi-predicate code? (Don't forget to override hashCode also).
After you do this, you can keep the Trainees that are in both lists using:
Set<Trainee> foundInBothList = new HashSet<>(dbMonthlyTraineeList);
foundInBothList.retainAll(new HashSet<>(csvTraineeList));
This solution is O(n) and so it will perform a lot better than your solution, which is O(n²). This is because the contains operation is constant-time on a Set.
But if you really want your code to compile, you just need to call the test method where you have the error:
return allBiPredicates.stream().reduce(BiPredicate::and)
.orElse((x,y)->true)
.test(dbTrainee, csvTrainee);

Related

how to avoid multiple if-else statements for vaildation in Java

I have lots of multiple if-else statements. For code optimization, I need to write one function for all if else logic. As of now my code structure is in below.
input request is in JSONObject(org.json.simple.JSONObject), which have more than 10 values.
String s = (String) inputObj.get("test");
String s1 = (String) inputObj.get("test");
String s2 = (String) inputObj.get("test");
String s3 = (String) inputObj.get("test");
if (s != null && s.trim().isEmpty()) {
if (s1 != null && s1.trim().isEmpty()) {
if (s2 != null && s2.trim().isEmpty()) {
if (s3 != null && s3.trim().isEmpty()) {
if (s4 != null && s4.trim().isEmpty()) {
........
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
} else {
return;
}
How to avoid this kind of looping and throw an error message in common method.
Advance thanks.
Consider adding all your strings to array or ArrayList of string, and looping thru each entry in it, and check them for null or emptiness.
You can try this.
void main() {
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
for(String s : sList){
try {
checkString(s);
}catch (Exception e){
//log or print the exception, however you like
}
}
}
void checkString(String s) throws Exception{
if(s!= null && !s.trim().isEmpty()){
//doStuff
}else{
throw new Exception("String is null or empty !!!");
}
}
You should also check this out.
public class YourClass{
private boolean isBlankDataPresent(JSONObject inputObj, String[] keys) throws Exception {
for (String key : keys) {
String input = (String) inputObj.get(key);
if( input == null || input.trim().isEmpty())
throw new Exception(key +" is Empty");
}
return false;
}
public boolean validateData(JSONObject inputObj, String[] keys) throws Exception {
boolean isBlankDataPresent= isBlankDataPresent(inputObj, keys);
if (!isBlankDataPresent) {
// do Your Stuff and return true
}
}
}
public Integer checkIsEmapty(String checkingString){
if(checkingString != null && !checkingString.trim().isEmpty()){
return 1;
}
return 0;
}
public String method(){
String s ="";
String s1 = "hi";
String s2 = "java";
String s3 = null;
String s4 = null;
Integer s1i = checkIsEmapty(s);
Integer s2i = checkIsEmapty(s1);
Integer s3i = checkIsEmapty(s2);
Integer s4i = checkIsEmapty(s3);
Integer s5i = checkIsEmapty(s4);
Integer total = s1i + s2i + s3i + s4i + s5i;
switch (total){
case 1 :
// To DO
case 2 :
// To DO
}
}
in switch used to checking the value, U can pass binary and Integer also
Like #Emre Acre mentioned,
List<String> sList = new ArrayList<>();
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
sList.add(inputObj.get("test"));
boolean allDataValid = sList
.stream()
.allMatch(s -> s != null && s.trim().isEmpty());
if(allDataValid) {
......
} else {
return;
}

Recursion - Expressions that evaluate to target

I am trying to understand recursion and solve the problem with
operators = ['','*', "+"]
input : "2224"
target : 24
output = {"22+2", "2+22", "24"}
Here's the code that I came up with. But it produces invalid output.
static List<String> output = new ArrayList<>();
static String[] generate_all_expressions(String s, long target) {
getExpressionsRecur(s, target, 0, null, 0);
String[] out = new String[output.size()];
return output.toArray(out);
}
static void getExpressionsRecur(String s, long target, int currentValue, String currExpression, int currIndex) {
if (currIndex == s.length()){
if (currentValue == target) {
output.add(currExpression);
}
return;
}
if (currentValue == target) {
output.add(currExpression);
return;
}
int currentPart = Integer.valueOf(s.substring(currIndex, currIndex+1));
if (currIndex == 0) {
getExpressionsRecur(s, target, currentPart, String.valueOf(currentPart), currIndex+1);
} else {
int value = Integer.valueOf(String.valueOf(currentValue) + String.valueOf(currentPart));
getExpressionsRecur(s, target, value , currExpression + "" + currentPart, currIndex+1);
getExpressionsRecur(s, target, (currentValue * currentPart), currExpression + "*" + currentPart, currIndex+1);
getExpressionsRecur(s, target, (currentValue + currentPart), currExpression + "+" + currentPart, currIndex+1);
}
}
It produces:
{22+2, 2*2+2*4, 2+2+2*4}
Can someone help me spot the errors?
First rethink your recursion since you're not hitting every case.
Write out cases if you need to, maybe start with a 3 digit number since there are less cases; In your code, for example, "24" is never evaluated on it's own.
You are calculating value wrongly for concatenation case. 22*2 will become 22*24.But you are saying this new expression has a value of 444 (should be 528) . You can't really use current value. Probably you can restructure your recursion to make it easier.
You can use this Expression object I threw together in replace of your expression String... call expression.toString() and expression.value() as necessary. But still you'll need changes to your basic recursion structure even after implementing this or similar Expression Object.
public class Expression{
Expression lExpression;
String lValue;
Expression rExpression;
String operator;
public Expression(String expression) {
int multIndex = expression.indexOf("*");
int addIndex = expression.indexOf("+");
if(multIndex == -1 && addIndex == -1) {
this.lExpression = new Expression(Integer.valueOf(expression));
return;
}
if(multIndex != -1 && multIndex < addIndex) {
this.lExpression = new Expression(expression.substring(0, addIndex));
this.operator = expression.substring(addIndex, addIndex+1);
this.rExpression = new Expression(expression.substring(addIndex+1));
}else {
if(addIndex == -1) {
addIndex = Integer.MAX_VALUE;
}
if(multIndex == -1) {
multIndex = Integer.MAX_VALUE;
}
int opIndex = multIndex < addIndex ? multIndex : addIndex;
this.lExpression = new Expression(expression.substring(0, opIndex));
this.operator = expression.substring(opIndex, opIndex+1);
this.rExpression = new Expression(expression.substring(opIndex+1)); }
}
public Expression(int value) {
this.lValue = String.valueOf(value);
this.lExpression = null;
this.rExpression = null;
this.operator = null;
}
#Override
public String toString() {
return (lExpression!=null ? lExpression : lValue) + (operator !=null ? operator: "") + (rExpression!=null ? rExpression : "");
}
public int value() {
if(lExpression == null) {
return Integer.valueOf(lValue);
}
if("*".equals(operator)) {
return lExpression.value() * rExpression.value() ;
}
if("+".equals(operator)) {
return lExpression.value() + rExpression.value();
}
return lExpression.value();
}
}

java compare multiple values and find best match

I need to find best matched employee salary in the DB records as:
Name: City: State:
A (null) (null)
A (null) DEL
(null) (null) (null)
A SAKET DEL
Match order should be:
1. NAME = name, STATE = state, CITY = city
2. NAME = name, STATE = state , CITY = NULL
3. NAME = name, STATE = NULL, CITY = NULL
4. NAME = NULL, STATE = NULL, CITY = NULL
Means if in a row where all attributes matches – it should be selected, if we do not have that kind of data we should go to next best option like select state and city as NULL, etc.
My code as below, is giving me correct results but I need a more efficient way.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
BigDecimal salaryWithState = null;
BigDecimal salaryWithName = null;
BigDecimal salaryWithNoMatch = null;
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = null;
if(results.getString("SALARY") != null){
salaryRslt = BigDecimal.valueOf(results.getDouble("SALARY"));
}
if(billerName != null && !billerName.equals("") && billerName.equals(request.getBillPaymentsalaryCalculateInfo().getBillerName())){
if(city != null && !city.equals("") && city.equals(request.getMsgRqHdr().getCity()) &&
state != null && !state.equals("") && state.equalsIgnoreCase(request.getMsgRqHdr().getstate())){
salary = salaryRslt;
break;
} else if((city == null || city.equals("")) && state != null && !state.equals("") &&
state.equalsIgnoreCase(request.getMsgRqHdr().getState())){
salaryWithState = salaryRslt;
} else if((city == null || city.equals("")) && (state == null || state.equals(""))){
salaryWithName = salaryRslt;
}
} else if((billerName == null || billerName.equals("")) && (city == null || city.equals("")) &&
(state == null || state.equals(""))){
salaryWithNoMatch = salaryRslt;
}
}
if(salary != null){
return salary;
} else if(salaryWithState != null){
salary = salaryWithState;
} else if(salaryWithName != null){
salary = salaryWithName;
} else if(salaryWithNoMatch != null){
salary = salaryWithNoMatch;
}
return salary;
}
EDIT: I dont want to use 3 extra variables: salaryWithState, salaryWithName, salaryWithNoMatch.
I want just to give the general idea how this can be implemented, so I haven't actually tested and checked if it will give you the right salary.
public BigDecimal getSalaryForBestMatch(ResultSet resultSet, PaymentSalaryInfo paymentSalaryInfo) {
Map<String, Supplier<String>> m1 = new HashMap<>();
m1.put("EMP_NAME", paymentSalaryInfo::getBillerName);
m1.put("STATE", paymentSalaryInfo::getState);
m1.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m2 = new HashMap<>();
m2.put("STATE", paymentSalaryInfo::getState);
m2.put("CITY", paymentSalaryInfo::getCity);
Map<String, Supplier<String>> m3 = new HashMap<>();
m3.put("CITY", paymentSalaryInfo::getCity);
Optional<String> salary = Optional.empty();
while(resultSet.next() && !salary.isPresent()) {
salary = apply(m1, resultSet);
//check salary and then apply(m2, resultSet) ....
}
return salary.isPresent() ? new BigDecimal(salary.get()) : null;
}
public Optional<String> apply(Map<String, Supplier<String>> filter, ResultSet resultSet) {
boolean allMatch = filter.entrySet().stream().allMatch(entry -> {
String value = resultSet.getString(entry.getKey());
return value != null && value.equals(entry.getValue().get());
});
return allMatch ? Optional.of(resultSet.getString("salary")) : Optional.empty();
}
I have written the same logic in a different way with using arrays. If your environment can afford to use arrays, you can use this code. But I have not tested the code.
private static BigDecimal getsalaryForBestMatch(ResultSet results, EmployeeRq request) throws Exception{
BigDecimal salary = null;
int matchCount = 0;
String rBillerName = request.getBillPaymentsalaryCalculateInfo().getBillerName();
String rCity = request.getMsgRqHdr().getCity();
String rState = request.getMsgRqHdr().getstate();
String [] truthArray = new String[] {rBillerName, rCity, rState};
while (results.next()) {
String billerName = results.getString("EMP_NAME") != null ? results.getString("EMP_NAME").trim() : null;
String city = results.getString("CITY") != null ? results.getString("CITY").trim() : null;
String state = results.getString("STATE") != null ? results.getString("STATE").trim() : null;
BigDecimal salaryRslt = results.getString("SALARY") != null ? BigDecimal.valueOf(results.getDouble("SALARY")): null;
String [] testArray = new String[] {billerName, city, state};
int localMatchCount = 0;
for(int i = 0; i < testArray.length; i++) {
if(testArray[i] != null && testArray[i].equals(truthArray[i]))
localMatchCount++;
else {
break;
}
}
if(localMatchCount >= matchCount){
matchCount = localMatchCount;
salary = salaryRslt;
}
}
return salary;
}

Replace custom list search with Java lambda expressions

I am looking to replace the following code with Java lambda expressions.In the below case I need to come out the loop if the match is found and also set boolean to true in order to assert a condition
Long teamId;
boolean matchFound = false;
List<WorkflowSubscriptions> workflowSubscriptionsList = fetchSubscriptions();
for (WorkflowSubscriptions workflowSubscriptions : workflowSubscriptionsList) {
for (WorkflowCompositeInfo workflowCompositeInfo : workflowSubscriptions.getWorkflowCompositeInfo()) {
if (workflowCompositeInfo.getId() > 0 && workflowCompositeId == workflowCompositeId.intValue()) {
teamId = Long.valueOf(workflowSubscriptions.getId());
matchFound = true;
}
}
}
if (!matchFound) {
throw new CustomParameterizedException("Workflow does not have valid subscriptions");
}
Try this:
fetchSubscriptions().stream()
.filter(p -> w.getId() > 0 && workflowCompositeId == w.intValue())
.limit(1)
.forEach(w -> {teamId = Long.valueOf(w); matchFound = true;} );
UPDATE
To avoid errors about modifying a final variable. You can simply create a value class:
public class MyValue {
public boolean matchFound;
public Long teamId;
}
Then:
final MyValue value = new MyValue();
fetchSubscriptions().stream()
.filter(p -> w.getId() > 0 && workflowCompositeId == w.intValue())
.limit(1)
.forEach(w -> {value.teamId = Long.valueOf(w); value.matchFound = true;} );
// Here, find your data in value.teamId and value.matchFound

Data Structure for keeping frequency count of pairwise data?

I have a table with hundred' of record where a field is paired with a similar field based on an id. I want to know what is a good data structure for keeping frequency counts for the number of times a pair has appeared together irrespective of the order they appeared in.
Sample data:
ID Feature
5 F1
5 F2
6 F1
6 F2
7 F3
7 F1
7 F2
8 F1
9 F1
10 F1
The sample output is:
F1 F2 F3
F1 0 3 1
F2 3 0 1
F3 1 1 0
One option is to sort all features and use a 2-dimensional int array to represent the pairwise data but then 2/3's of the array is useless/duplicate. For example array[i][i] = 0 and array[i][j] = array[j][i]. Given that I have hundreds of features, this approach won't work.
I thought of using a map but then the key needs to represent a pair e.g. (F1,F3). I am hoping for other solutions too. If there are none I will use a map.
Create a class, say MyPair to use for hash keys that stores pairs of your items and overrides Object#equals(...) (and Object#hashCode()) so that order doesn't matter (e.g. by ordering lexicographically).
Create a Map<MyPair,Integer> to store the frequency count of your pairs.
class MyPair {
public final String feature1;
public final String feature2;
public MyPair(String s1, String s2) {
// Order features so comparison is order-independent.
if (s1.compareTo(s2) <= 0) { // TODO: null check
feature1 = s1;
feature2 = s2;
} else {
feature1 = s2;
feature2 = s1;
}
}
#Override public int hashCode() {
return (s1 + s2).hashCode(); // TODO: cache for performance.
}
#Override public boolean equals(that) {
return (that instanceof MyPair)
&& (that.feature1.equals(this.feature1))
&& (that.feature2.equals(this.feature2));
}
}
Then can hash pairs as expected:
Map<MyPair,Integer> freq = new HashMap<MyPair,Integer>();
MyPair pair1 = new MyPair("F1", "F2");
freq.get(pair1); // => null
freq.put(pair1, 1);
MyPair pair2 = new MyPair("F2", "F1");
freq.get(pair2); // => 1
This is simple algorithm. I assume that data is initially sorted. It is not maybe written as good as I wanted to be, but It must only shows you the proper path :)
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class NeighborListExample {
static class Pair {
private String feature;
private int cnt = 1;
Pair(String feature) {
this.feature = feature;
}
void incr() {
cnt++;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((feature == null) ? 0 : feature.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pair other = (Pair) obj;
if (feature == null) {
if (other.feature != null)
return false;
} else if (!feature.equals(other.feature))
return false;
return true;
}
#Override
public String toString() {
return "(" + feature + ", " + cnt + ")";
}
}
static Map<String, List<Pair>> feature2neighbors = new HashMap<>();
private static int getId(Object[][] data, int i) {
return ((Integer) data[i][0]).intValue();
}
private static String getFeature(Object[][] data, int i) {
return data[i][1].toString();
}
private static void processFeatures(String[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if (i != j) {
List<Pair> pairs = feature2neighbors.get(array[i]);
if (pairs == null) {
pairs = new LinkedList<>();
feature2neighbors.put(array[i], pairs);
}
Pair toAdd = new Pair(array[j]);
int index = pairs.indexOf(toAdd);
if (index == -1) {
pairs.add(toAdd);
} else {
pairs.get(index).incr();
}
}
}
}
}
static void print(Map<String, List<Pair>> feature2neighbors) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, List<Pair>> e : feature2neighbors.entrySet()) {
builder.append(e.getKey()).append(" -> ");
Iterator<Pair> it = e.getValue().iterator();
builder.append(it.next().toString());
while(it.hasNext()) {
builder.append(" ").append(it.next().toString());
}
builder.append("\n");
}
System.out.println(builder.toString());
}
public static void main(String[] args) {
//I assume that data is sorted
Object[][] data = { { 5, "F1" }, //
{ 5, "F2" }, //
{ 6, "F1" }, //
{ 6, "F2" }, //
{ 7, "F3" }, //
{ 7, "F1" }, //
{ 7, "F2" }, //
{ 8, "F1" }, //
{ 9, "F1" }, //
{ 10, "F1" }, //
};
List<String> features = new LinkedList<>();
int id = getId(data, 0);
for (int i = 0; i < data.length; i++) {
if (id != getId(data, i)) {
processFeatures(features.toArray(new String[0]));
features = new LinkedList<>();
id = getId(data, i);
}
features.add(getFeature(data, i));
}
print(feature2neighbors);
}
}
Out:
F1 -> (F2, 3) (F3, 1)
F3 -> (F1, 1) (F2, 1)
F2 -> (F1, 3) (F3, 1)

Categories

Resources