Processing HashMap of crew details - java

Enviroment: Java 7
I have a hashmap 'pdf' of Aircrew details that has the following key structure:
public static Map<String, String> pdf;
PilotName JONES
PilotFirstname Jim
PilotID 12345
PilotLicense AAD987
PilotDOB 12/12/2001
PilotGender Male
PilotEthnicity
PilotAddress 123 Any Street
CopilotName SMITH
CopilotFirstname Thomas
CopilotID 987654
CopilotLicense AAB9475
CopilotAddress 456 Any Street
CopilotDOB 12/03/1987
CopilotGender Male
CopilotEthnicity European
CabinManagerSurname
CabinManagerFirstName BROWN
CabinManagerID 48573
CabinManagerDOB
CabinManagerGender
CabinManagerEthnicity
CabinManagerAddress
Hostess1Surname
Hostess1FirstName
HostessID
Hostess1DOB
Hostess1Gender
Hostess1Ethnicity
Hostess1Address 789 Any Street
Hostess2Surname EDWARDS
Hostess2FirstName Mary
HostessID 475804
Hostess2DOB 11/10/1990
Hostess2Gender Female
Hostess2Ethnicity European
Hostess2Address
Hostess3Surname
Hostess3FirstName
Hostess3ID 489282
Hostess3DOB
Hostess3Gender
Hostess3Ethnicity
Hostess3Address
NB: The field names for crew and pilots are different (Surname/Name Firstname/FirstName).
I want to test if any of certain fields are not empty then call createPerson() method.
The fields to be tested differ for Cabin Crew from Pilots.
I made this attempt but the code is ugly:
List<String> pilotRoles = ["Pilot", "Copilot"];
List<String> cabinRoles = ["CabinManager", "Hostess1", "Hostess2", "Hostess3"];
for (String role : pilotRoles) {
if ( String.isNotNullOrEmpty(pdf.get(pilotRole +"Name")) || String.isNotNullOrEmpty(pdf.get(pilotRole +"Firstname")) || String.isNotNullOrEmpty(pdf.get(pilotRole +"ID")) || String.isNotNullOrEmpty(pdf.get(pilotRole +"License"))) {
listPeople.add(createPerson(pdf, pilotRole));
for (String role : cabinRoles) {
if ( String.isNotNullOrEmpty(pdf.get(cabinRole +"Surname")) || String.isNotNullOrEmpty(pdf.get(cabinRole +"FirstName")) || String.isNotNullOrEmpty(pdf.get(cabinRole +"ID")) ) {
listPeople.add(createPerson(pdf, cabinRole));
For the above data the createPerson routine would be entered for both pilots as at least 1 of the tested fields is not null or empty.
The createPerson routine would NOT be entered for hostess1 and all of the tested fields are null or empty.
Is there a more elegant way? If so how.

I'd try something like this: In addition to your lists of roles, create an additional list that holds the names of all attributes, like "Name" etc.
Then you can then create a function to filter your roles for fields that are missing in your pdf map, like this:
private List<String> getRolesOfMissingUsers(Map<String, String> pdf, List<String> roles, List<String> attributes) {
return roles.stream().filter(role -> attributes.stream().map(attribute -> role + attribute)
.anyMatch(roleAttribute -> StringUtils.isNotBlank(pdf.get(roleAttribute))))
.collect(toList());
}
You can then use the result of this method to create your missing users. Here is an example just for your pilot rules:
for (String role : getRolesOfMissingUsers(pdf, pilotRoles, Arrays.asList("Name", "Firstname", "ID", "License"))) {
listPeople.add(createPerson(pdf, role));
}
EDIT: I noticed you're on Java 7, so you could try this instead:
for (String role : pilotRoles) {
for (String attribute : Arrays.asList("Name", "Firstname", "ID", "License")) {
if (StringUtils.isNotBlank(pdf.get(role + attribute))) {
listPeople.add(createPerson(pdf, role));
break;
}
}
}
If you extract this to a method and pass the list of attributes as a parameter, you should also be able to use this for your crew and pilot list.

Related

Get a filtered list of objects defined inside a MongoDB document

I work with Spring Boot and MongoDB. I am looking for a solution to get a list of hobbies but not all of them. The hobbies have to meet the conditions (name or description have to include searched phrase).
#Document("persons")
#Data
class Person {
#Id
private String personId;
private String name;
private List<Hobby> hobbies;
}
#Data
class Hobby {
private String name;
private String description;
private String notImportantField;
}
Example
I want to get a person with reduced list of hobbies (all the hobbies have to contain searched phrase in one of its field).
Person document from the database
{
"_id" : ObjectId("id1"),
"name" : "some person",
"hobbies" : [
{
"name" : "A",
"description" : "AB",
"notImportantField" : "ABCDEF"
},
{
"name" : "ABC",
"description" : "ABCD",
"notImportantField" : "ABCDEF"
}
]
}
What I want to receive:
I want person with id id1 and I am looking for phrase ab in person's hobbies. I should get a list with 2 hobbies (first hobby's description contains ab, second hobby's name and description contains ab)
I want person with id id1 and I am looking for phrase d in person's hobbies. I should get a list with 1 hobby (second hobby's description contains d)
I tried something like this but I get a person with all of its hobbies.
#Repository
interface PersonRepository extends MongoRepository<Person, String> {
#Query("{'$and': [" +
"{'_id': :#{#personId}}," +
"{'$or':[" +
"{'hobbies.name': {$regex: :#{#searchPhraseRegex}, $options: 'i'}}," +
"{'hobbies.description': {$regex: :#{#searchPhraseRegex}, $options: 'i'}}" +
"]}" +
"]}")
List<Person> method(#Param("personId") String personId, #Param("searchPhraseRegex") String searchPhraseRegex);
}
The result method should return a person with filtered hobbies or only list of hobbies.
Thanks in advance for help.
UPDATE: RESOLVED
Thanks #user20042973 for the help :) I used your query and change it a little to match my query in Mongo repository. It works as I expected. The result method is:
#Repository
interface PersonRepository extends MongoRepository<Person, String> {
#Aggregation(pipeline = {
"{'$match': {'_id': :#{#personId}}}",
"{'$addFields': {'hobbies': {'$filter': {" +
"'input': '$hobbies', " +
"'cond': " +
"{'$or': [" +
"{'$regexMatch': {'input': '$$this.name', 'regex': :#{#searchPhraseRegex}, 'options': 'i' }}," +
"{'$regexMatch': {'input': '$$this.description', 'regex': :#{#searchPhraseRegex}, 'options': 'i' }}" +
"]}" +
"}}}}"
})
Optional<Person> findPersonByIdAndFilterHobbies(#Param("personId") String personId, #Param("searchPhraseRegex") String searchPhraseRegex);
}
Note: For searching e.g. ab we need to pass .*ab.* as a method argument searchPhraseRegex.
Helpfully, getting a filtered list of objects from an array can be done using the $filter operator. The operator takes an array as input and can process each item individually via an expression. In your situation that expression would include a $regexMatch to look for the value in the string(s) of interest. So the stage would look something like this:
{
"$addFields": {
hobbies: {
$filter: {
input: "$hobbies",
cond: {
$or: [
{
$regexMatch: {
input: "$$this.name",
regex: "d",
options: "i"
}
},
{
$regexMatch: {
input: "$$this.description",
regex: "d",
options: "i"
}
}
]
}
}
}
}
}
Sample Mongo Playground example is here.
From your question it's not quite clear if you will separately be using this as selection criteria for the document itself. I've left the leading $match stage to just use _id for the moment, but of course you can add any other filters to it (such as 'hobbies.name': /d/i) as needed.
I'm not personally familiar enough with Spring Boot to say what the exact syntax is to create such a pipeline, but I do know that aggregations are supported.

Java: 1 key with multiple values using data structure

I have a list of objects having code and user name. I can get the code and username with getCode() and getUserName(). I can think of using MultiMap if I must display only code and list of usernames associated for each code. But how to display all three details (code, usercount, usernames) with different data types using Java?
Code - String (Key)
UserCount - Integer (Value)
UserNames - List (Value)
How to display in the below format using Java?
Code UserCount UserNames
Abc 2 Jack, Mary
Def 1 Steven
Ghi 3 James, Ray, Jim
If I understand, you have a List<YourObject> where YourObject is the class :
public class YourObject {
public String code;
public String userName;
// getters and constructor(s)
}
When you say :
I must display only code and list of usernames associated for each code
It means that in your List<YourObject>, if two objects A and B have the same code value X, you want to display something like "Code X ; usernames = [A.getUserName(), B.getUserName()]" ?
But you also want to add a userCount attribute which is the size of the usernames list (in my above exemple : 2).
I think you can create a utility method that takes your List<YourObject> as parameter and return a Map<String,List<String>> where the key string is the code and the List of strings is the usernames.
Something like : (Sorry for the stream version, I was too lazy to write more lines) :
public static Map<String,List<String>> process(List<YourObject> list) {
return list.stream().collect(Collectors.toMap(YourObject::getCode, x -> list.stream().filter(e -> x.getCode().equals(e.getCode())).map(YourObject::getUserName).collect(Collectors.toList()), (x,y) -> x));
}
The display you wanted :
process(yourList).forEach((k,v) -> {
System.out.println("Code : " + k + " | userCount :" + v.size() + " | Usernames =" + v)
});

Matching a value of HashMap to values in a HashSet

I am trying to match the values of the key in the contextMap to a set of string. I know there is a clear match but despite that I get an empty map in my output. Is it correct to use the contains() method in this case?
The context map contents look like:
[Rhode Island ]=[leftWords: [fellow], rightWords: [solution,
provider]]
And the roleContexts set looks like :
[recent performance, has earned , holdings in , senior vice president
of, solution provider , pharmaceuticals, has launched ]
Here the word solution provider should be a match, thus the key Rhode Island should by put in the output map with he corresponding role (Location in this case).
for(Entry<Set<String>, Set<Words>> entry : contextMap.entrySet()) {
for (String key : entry.getKey()){
for (Words value : entry.getValue()) {
for(SemanticRole role : roleContextsMap.keySet())
{
Set<String> roleContexts = roleContextsMap.get(role);
if(roleContexts.contains(value.getLeftWords().toString().replaceAll("\\[|\\]","").replaceAll(","," ").toLowerCase())
|| roleContexts.contains(value.getRightWords().toString().toString().replaceAll("\\[|\\]","").replaceAll(","," ").toLowerCase()))
{
Set<String> roleStrings = roleStringsMap.containsKey(role)
? roleStringsMap.get(role) : new HashSet<String>();
roleStrings.add(key);
roleStringsMap.put(role, roleStrings);
}
}

Filter a List based on words from String Array

I have a result set having List<Employees> sent by another application.
class Employee{
Long id;
String name;
String gender;
List<String> projects;
// Getters
// Setters
}
I need to write a method or lambda expression to filter the List using a bunch of query words (String[]) passed from the UI.
Any word in the String[] can match any variable (id, name, gender, projects). All List which have a match should be returned. part of name should also match e.g.: "john" should match List 1 and 3 in the example.
List<Employee> filter (empList, queryWords) {
// code
}
Can you point me in the right direction to achive this?
example:
List:
1. 121, john doe , male , (proj1)
2. 125, sam , female, (proj4 proj5 proj9)
3. 129, john lam , male , (proj1 proj2 proj5)
4. 143, peter pan , male , (proj4 proj8)
5. 151, linda , female, (proj8 proj7 proj3 proj11)
Search Query Words:
1. "female" "proj3"- should return only No.5
2. "proj5" - should return only No.2 and 3
3. "john" - should return No.1 and 3
4. "pan" - should return No.4
public List<Employee> filter(empList, queryWords){
List<Employee> result = new ArrayList<Employee>();
// look at each employee in the list
for(Employee employee : empList){
// look at each query string
for(String queryWord : queryWords){
// if any of the employee fields matches the query word,
// add it to our list and move to next employee
if(employee.name.equals(queryWord) ||
employee.gender.equals(queryWord) ||
employee.id.toString().equals(queryWord) ||
isQueryInList(queryWord, employee.projects)) {
// add it to your results
result.add(employee);
// quit looking at the rest of the queryWords,
// we found one, thats enough, move on to the next employee
break;
}
}
}
return result;
}
private boolean IsQueryInList(String queryWord, List<String> items){
//check each item in the list to see if it matches the queryWord
for(String item : items){
if(queryWord.equals(item)) {
return true;
}
}
//if we didn't find any item that matches, return false
return false;
}
Write a method
private boolean employeeMatchesWord(Employee employee, String word)
that returns true if at least one field of the employee matches the given word.
Then use
return empList.stream()
.filter(employee -> Arrays.stream(queryWords)
.anyMatch(word -> employeeMatchesWord(employee, word))
.collect(Collectors.toList());
You could convert the query words array to a Set, create a Set of properties from all the employee's members, and use retainAll to determine which employees have at least one of the query words:
public static List<Employee> filter (List<Employee> empList, String[] queryWords) {
Set<String> queryWordsSet = new HashSet<>(Arrays.asList(queryWords));
return empList.stream().filter(e -> {
Set<String> properties = new HashSet<>(e.getProjects());
properties.addAll
(Arrays.asList(e.getId().toString(), e.getName(), e.getGender()));
properties.retainAll(queryWordsSet);
return !properties.isEmpty();
}).collect(Collectors.toList());
}
EDIT:
As JB Nizet commented, the retainAll can be elegantly replaced with an anyMatch expression:
public static List<Employee> filter (List<Employee> empList, String[] queryWords) {
Set<String> queryWordsSet = new HashSet<>(Arrays.asList(queryWords));
return empList.stream().filter(e -> {
Set<String> properties = new HashSet<>(e.getProjects());
properties.addAll
(Arrays.asList(e.getId().toString(), e.getName(), e.getGender()));
return properties.stream().anyMatch(queryWordsSet::contains);
}).collect(Collectors.toList());
}

Recursivity missing for loop case in java

my goal is to associate all team members to teamleaders but hierarchically. eg : Bill is a top team leader, John and Alice are one level down, Mary is one level down to John and Alice,etc...
Team leader Team members
========= ==========
Bob ""
Marry Bob
John Marry
Alice Marry
Bill John,Alice
So I use
final static SetMultimap<String, String> teamMap = HashMultimap
.create();
and I want to get for each Team leader the full team members list he leads
eg : For Alice as a Team Leader, her full team members list is Marry, Bob.
For Bill as a Team Leader, his full team members list is John, Alice, Marry, Bob
Since recursivity seems to answer to my problem, I use this
public static List<String> getTeamMembers(String teamLeader) {
List<String> teamMembers = teamMembersMap.get(teamLeader);
fullTeamMembers.addAll(teamMembers);
for (String teamMember : teamMembers) {
if (teamMember.equalsIgnoreCase("")) {
**// if teamMember is John go to Marry then Bob and return recursively
// without treating Alice loop!**
return fullTeamMembers;
} else {
return getTeamMembers(teamMember);
}
}
**// compiler error otherwise force to return something here
// maybe in case teamMembers is empty****
return null;
}
And my call
for (String teamLeader : teamMembersMap.keySet()) {
System.out.println("********************************");
System.out.println("**************teamLeader :" + teamLeader);
fullTeamMembers.clear();
List<String> fullTeamMembers = getTeamMembers(teamLeader);
teamMembersMap.putAll(teamLeader, fullTeamMembers);
}
But I am stucked on the two point in comment in the code : the loop issue and the return compiler error. Any Help is welcomed to solve this problem in a better way.
Of course it tells you to put a return statement there. Your teamMembers could be empty or even NULL. Other than a few missing null/empty checks, there is nothing wrong with your code.
If you really want an improvement and null safety, here's my suggestion:
public static List<String> getTeamMembers(final String teamLeader)
{
final List<String> result = new ArrayList<>(); // fullTeamMembers
final List<String> teamMembers = teamMembersMap.get(teamLeader); // Can be NULL or empty
if (teamMembers == null || teamMembers.isEmpty())
return result; // Return empty array
// Else, carry on
result.addAll(teamMembers);
for (String teamMember : result)
if ( !teamMember.equalsIgnoreCase("") )
return getTeamMembers(teamMember); // Go recursively if not ""
// Return result from map
return result;
}

Categories