I'm having a possible solution to the problem in my head but I don't quite know how to do that with code. I got stuck with invoking a method in a method in Java.
I have this code:
public Student getStudent(String queryWord){
//the queryWord here should actually be the String result that returnQueryColumn returns
}
private static Map<String, String> returnMap(String myQuery){
String[] params = myQuery.split("=");
Map<String, String> myMap = new HashMap<String, String>();
String myKey = params[0];
String myValue = params[1];
//myKey should be for example firstName, myValue should be the ACTUAL first name of the student
myMap.put(myKey,myValue);
return myMap;
}
private static String returnQueryColumn(Map<String, String> myMap){
//here I want to get just the KEY from the myMap(from the returnMap method)
//that key should be the column in my database, so I need this so that later on I can compare if the given key (firstName) is present in the database as a column
String queryWord = returnMap().get(); //query should get firstName in my case
return queryWord;
}
I know this code doesn't work, but I need some help, how can I achieve what I have in mind? I'm stuck at this - how can I invoke a method in other method, and make the string that is being returned in the first method to be a parameter in the second one.
Let's say you have Student class:
public class Student {
String fullName;
public Student(String fullName) {
this.fullName = fullName;
}
}
And, if I understood your intentions right, Main class can look like this.
Sample code prints student fullName property.
public class Main {
public static void main(String[] args) {
Student student = getStudent("john=John Appleseed");
System.out.println(student.fullName);
}
public static Student getStudent(String myQuery) {
return returnQueryColumn(myQuery);
}
private static Map<String, Student> returnMap(String myQuery){
String[] params = myQuery.split("=");
Map<String, Student> myMap = new HashMap<String, Student>();
String myKey = params[0];
String myValue = params[1];
Student student = new Student(myValue);
myMap.put(myKey, student);
return myMap;
}
private static Student returnQueryColumn(String myQuery) {
String[] params = myQuery.split("=");
String key = params[0];
return returnMap(myQuery).get(key);
}
}
Related
I am trying to find string with max length of a given attribute of java. I will pass the attribute name as string into the method which will return me the string value of max length.
class Employee {
private String name;
private String designation;
private List<Address> address;
private ContactInfo contactInfo;
....
getter setter
}
class Address {
private String city;
private String state;
private String country;
......
getter setter
}
class ContactInfo {
private String mobileNumber;
private String landlineNumber;
....
getter setter
}
I have some data just like below:
ContactInfo contactInfo = new ContactInfo("84883838", "12882882");
Address address1 = new Address("city111", "state111", "country111");
Address address2 = new Address("city111111", "state11112", "country1112");
Employee employee1 = new Employee("xyz", "uyyy", List.of(address1, address2), contactInfo);
private String findStringWithMaxLength(String attribute) {
return employeeList.stream()
....
}
In above case, if I provide attribute value as "city" then it should return me the value "city111111" because of maximum string length.
If we have child objects and list of objects, how do I traverse with the given attribute.
You can create a method that take a list of employees and a function to get the specific attribute like this:
private String findStringWithMaxLength(List<Employee> employees, Function<Employee, String> function) {
return employees.stream()
.map(function)
.max(Comparator.comparing(String::length))
.orElseThrow(() -> new IllegalArgumentException("Empty list"));
}
and to call the method you can use:
findStringWithMaxLength(employees, Employee::getName)
findStringWithMaxLength(employees, Employee::getDesignation)
findStringWithMaxLength(employees, Employee::getAddress)
Note that the method will throw an exception if the list is empty, if you wont throw an exception, then you can replace it with orElse(withDefaultValue)
You can do it using reflection but here is a better "typesafe" way.
Let the class:
#Getter
#Setter
#AllArgsConstructor
static class Employee {
private String name;
private String designation;
private String address;
}
with getters and let the list
static List<Employee> employeeList = asList(
new Employee("xyz1", "abc1234", "address 123"),
new Employee("xyz123", "abc123", "address 1234"),
new Employee("xyz1234", "abc12", "address 12")
);
then, you can define a generic function able to traverse any String field
static Optional<String> findStringWithMaxLength(Function<Employee, String> getter) {
return employeeList.stream().map(getter).max(Comparator.comparingInt(String::length));
}
now, you can apply every getter to that function
for(Function<Employee, String> getter: Arrays.<Function<Employee, String>>asList(
Employee::getName,
Employee::getDesignation,
Employee::getAddress))
System.out.println(findStringWithMaxLength(getter));
with output
Optional[xyz1234]
Optional[abc1234]
Optional[address 1234]
(the optional is required since the list could be empty).
The given answers work fine. I'd like to use an enum in this case. If a method changes in the Employee class, you only have to change the enum, not every call using it:
enum EmployeeField {
NAME(Employee::getName),
DESIGNATION(Employee::getDesignation),
ADDRESS(Employee::getAddress);
private final Function<Employee, String> getter;
EmployeeField(Function<Employee, String> getter) {
this.getter = getter;
}
public Function<Employee, String> getGetter() {
return getter;
}
}
private static final List<Employee> employeeList = Arrays.asList(
new Employee("xyz1", "abc", "address 1"),
new Employee("xyz123", "abc", "address 1"),
new Employee("xyz1234", "abc", "address 1")
);
public static void main(String[] args) {
Optional<String> longestName = findStringWithMaxLength(EmployeeField.NAME);
if (longestName.isPresent()) {
System.out.println("Longest name = " + longestName.get());
} else {
System.out.println("No longest name");
}
}
private static Optional<String> findStringWithMaxLength(EmployeeField employeeField) {
return employeeList.stream()
.map(employee -> employeeField.getGetter().apply(employee))
.max(Comparator.comparing(String::length));
}
EDIT for your city use case, something along those lines:
Add an enum AddressField on the same model as the EmployeeField
enum AddressField {
CITY(Address::getCity);
....
}
then add a method
private static Optional<String> findStringWithMaxLength(List<Address> addressList, AddressField addressField) {
return addressList.stream()
.map(employee -> addressField.getGetter().apply(employee))
.max(Comparator.comparing(String::length));
}
and then add a CITY enum to your EmployeeField enum:
LANDLINE_NUMBER(employee -> employee.getContactInfo().getLandlineNumber()),
CITY(employee -> findStringWithMaxLength(employee.getAddress(), AddressField.CITY).get());
As part of my assignment, we are given an interface which we can't change, and some pre-defined tests to use to develop our methods.
The interface is as follows:
public interface ContactTracer {
public void addCourse(String courseID, String courseName);
public void addStudent(String id, String name, String email);
public void loadStudentCourseList(Map<String, String> enrolments);
public String getStudentName(String studentID);
public String getStudentEmail(String studentID);
public String getCourseForStudent(String studentID);
public String getCourseName(String courseID);
public List<String> findStudentsForCourse(String courseID);
public List<String> findContacts(String studentID);
}
The particular test I'm running:
#Test
void testLoadStudentCourseList() {
ContactTracer ct = new PartOneContactTracer();
Map<String, String> enrolments = new HashMap<>();
enrolments.put("S101", "SOFT01");
enrolments.put("S102", "NET02");
enrolments.put("S103", "SOFT01");
ct.loadStudentCourseList(enrolments);
List<String> students = ct.findStudentsForCourse("SOFT01");
assertEquals(2, students.size());
assertTrue(students.contains("S101"));
assertTrue(students.contains("S103"));
}
And the method I need to create from the test:
#Override
public List<String> findStudentsForCourse(String courseID) {
return null;
}
I can tell from the test that within the findStudentsForCourse method I need to create List<String> students and fill it with matching students from the enrolments Map, and then return the the List.
I have created the following fields:-
public class PartOneContactTracer implements ContactTracer {
private Map<String, String> courseDetails = new HashMap<>();
private Map<String, String> studentName = new HashMap<>();
private Map<String, String> studentEmail = new HashMap<>();
private Map<String, String> enrolments = new HashMap<>();
...
}
What I can't get my head around is how to pass the data in the enrolments Map into the students List in order to make the Assertion in the test true.
You don't need to create a list and fill it just search through the map all the keys (students) that has the value (course id):
public List<String> findStudentsForCourse(String courseId) {
return enrolments.entrySet().stream()
.filter(e -> e.getValue().equals(courseId))
.map(e -> e.getKey())
.collect(Collectors.toList());
}
Scenario:
I'm working with an unusual external API in which every attribute is a map with multiple values.
In order to convert this response into simple Java objects, I had to do some dirty unboxing. Below is one of the typical java class. As you can see how I'm unboxing data from response and mapping them to my java class:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import java.time.LocalDate;
import java.util.Map;
import static com.my.util.BaseUtil.unbox;
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class PartyDetailsDto {
private String partyId;
private String partyType;
private String title;
private String firstName;
private String lastName;
private String middleName;
private LocalDate dateOfBirth;
#JsonProperty(value = "partyId")
public void unboxPartyId(Map<String, String> data) {
this.partyId = unbox(data, "evidenceId");
}
#JsonProperty(value = "partyType")
public void unboxPartyType(Map<String, String> partyType) {
this.partyType = unbox(partyType, "value");
}
#JsonProperty(value = "individual")
public void unboxIndividualDetails(Map<String, Object> individual) {
Map<String, String> title = (Map<String, String>) individual.get("title");
Map<String, String> firstName = (Map<String, String>) individual.get("firstName");
Map<String, String> lastName = (Map<String, String>) individual.get("lastName");
Map<String, String> middleName = (Map<String, String>) individual.get("middleName");
Map<String, String> dateOfBirth = (Map<String, String>) individual.get("birthDate");
this.title = unbox(title, "value");
this.firstName = unbox(firstName, "value");
this.lastName = unbox(lastName, "value");
this.middleName = unbox(middleName, "value");
this.dateOfBirth = LocalDate.parse(unbox(dateOfBirth, "value"));
}
}
This is the sample util method - unbox - which I've created in order to avoid writing such ugly code. Right now, it only works for cases where String is returned.
import java.util.Map;
public class BaseUtil {
// TODO: Make this method generic
public static String unbox(Map<String, String> data, String key) {
if (data != null && data.containsKey(key)) {
return data.get(key);
}
return null;
}
}
I'm trying to convert above method into a generic one where I could specify the return type dynamically and cast the returned data accordingly.
Can anyone help me out in creating one?
I've tried this:
public static <T> T unbox(Map<String, String> data, String key, Class<T> type) {
if (data != null && data.containsKey(key)) {
return (type) data.get(key);
}
return null;
}
But it obviously doesn't work but in theory that's the kind of solution that I'm expecting.
EDIT: Here's a sample input of complex type:
// The associatePartyRole is a list of Stings.
#JsonProperty(value = "associatedPartyRole")
public void unboxAssociatedPartyRole(Map<String, Object> data) {
this.associatedPartyRole = unbox(data, "value", List.class);
// Compilation error: Need list, provided object.
}
EDIT 2: Here's the final solution:
PartyDetailsDto.java
public class PartyDetailsDto implements Serializable {
private static final long serialVersionUID = 3851075484507637508L;
private String partyId;
private String partyType;
private String title;
private String firstName;
private String lastName;
private String middleName;
private LocalDate dateOfBirth;
#JsonProperty(value = "partyId")
public void unboxPartyId(Map<String, String> data) {
this.partyId = unbox(data, "evidenceId");
}
#JsonProperty(value = "partyType")
public void unboxPartyType(Map<String, String> partyType) {
this.partyType = unbox(partyType, "value");
}
#JsonProperty(value = "individual")
public void unboxIndividualDetails(Map<String, Object> individual) {
this.title = unbox(unbox(individual, "title", Map.class), "value");
this.firstName = unbox(unbox(individual, "firstName", Map.class), "value");
this.lastName = unbox(unbox(individual, "lastName", Map.class), "value");
this.middleName = unbox(unbox(individual, "middleName", Map.class), "value");
this.dateOfBirth = LocalDate.parse(unbox(unbox(individual, "title", Map.class), "value"));
}
}
BaseUtil.java
public class BaseLineUtil {
public static <T> T unbox(Map<String, Object> data, String key, Class<?> ofType) {
return Optional.ofNullable(data)
.map(m -> (T) ofType.cast(m.get(key)))
.orElse(null);
}
public static <T> T unbox(Map<String, T> data, String key) {
return Optional.ofNullable(data)
.map(m -> (T) m.get(key))
.orElse(null);
}
}
Thanks #deduper #davidxxx for your answers.
Maybe that :
public static <T> T unbox(Map<String, T> data, String key) {
if (data != null && data.containsKey(key)) {
return data.get(key);
}
return null;
}
Here T implies T extends Object.
That you can use so with any class:
Map<String, Integer> map = ...;
Integer value = unbox(map, "key");
Note that you could simplify your implementation such as :
public static <T> T unbox(Map<String, T> data, String key) {
return Optional.ofNullable(data)
.map(m -> m.get(key))
.orElse(null);
}
It is also more efficient (a single map access)
OP comment :
I followed your solution but it doesn't seem to work when the return
type is supposed to be a list or an array. How would i handle that
case
That is surprising. It should work. Try that sample code :
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("key", 100);
Integer value = unbox(map, "key");
System.out.println(value);
Map<String, List<Integer>> mapOfList = new HashMap<>();
mapOfList.put("key", Arrays.asList(1, 2));
List<Integer> valueList = unbox(mapOfList, "key");
System.out.println(valueList);
Map<String, int[]> mapOfArray = new HashMap<>();
mapOfArray.put("key", new int[] { 1, 2, 3 });
int[] valueArray = unbox(mapOfArray, "key");
System.out.println(Arrays.toString(valueArray));
}
It outputs :
100
[1, 2]
[1, 2, 3]
If that is not what you are looking for. Please rewrite your question by specifying exactly what you want to perform.
Edit after requiement change :
public void unboxIndividualDetails(Map<String, Object> individual) {...}
In fact here you want to perform unsafe casts. To achieve that you don't need to pass a Class instance and that not will not make your code more safe type either.
What you want is telling to the compiler to accept that the object that is declared as Object be assigned to a more specific type variable.
In terms of cast logic that looks like :
Object o = Integer.valueOf(100);
Integer i = (Integer)o;
By declaring a parameterized generic type method, the compiler infers T from the target type (the variable type that received the call method return), so you can do just return (T)myObject.
Concrete code :
public static <T> T unboxUnsafe(Map<String, Object> data, String key) {
return Optional.ofNullable(data)
.map(m -> (T)m.get(key))
.orElse(null);
}
And here a sample test :
public static void main(String[] args) {
Map<String, Object> mapOfObjects = new HashMap< >( );
mapOfObjects.put("longKey", 1L );
mapOfObjects.put("stringKey", "hello" );
Long l = unboxUnsafe(mapOfObjects, "longKey");
System.out.println(l);
String s = unboxUnsafe(mapOfObjects, "stringKey");
System.out.println(s);
}
Output :
1
hello
„…I followed [#davidxx's] solution but it doesn't seem to work when the return type is supposed to be a list or an array. How would i handle that case?…“
Through a process I call „EDD“ („Experiment-driven Development“) the following way to handle those cases emerged…
public static < T > T unbox( Map< String, T > data, String key, Class< ? > ofType ) {
if ( data != null && data.containsKey( key ) ) {
return (T)ofType.cast( data.get( key ) ) ;
}
return null;
}
You can observe in main(String[]) that the following calls successfully return the expected result…
...
List< String > unBoxedList = unbox( mapOfLists, foo, List.class );
...
List< ? >[ ] unBoxedArrayOfLists = unbox( mapOfArrayOfLists, "bar", List[ ].class );
...
String unBoxedString = unbox( mapOfStrings, foo, String.class );
...
Integer unBoxedInteger = unbox( mapOfIntegers, bar, Integer.class );
...
Click the green Start button at the top of the page in the link above, to run the experiment.
After feedback in the comments from #saran3h that clarified his use case, the following refactor emerged from a subsequent iteration of the experiment…
public class BaseUtil {
public List<Object> associatedPartyRole ;
// TODO: Make this method generic
public static < T > T unbox( Map< String, T > data, String key, Class< ? > ofType ) {
if ( data != null && data.containsKey( key ) ) {
return (T)ofType.cast( data.get( key ) ) ;
}
return null;
}
public void unboxAssociatedPartyRole(Map<String, Object> data) {
this.associatedPartyRole = (List)unbox(data, "foo", Object.class);
}
}
That new case was successfully tested with…
...
private static final Map<String, Object> mapOfObjects = new HashMap< >( );
...
mapOfObjects.put( foo, (Object)mapOfLists.get( foo ) );
...
BaseUtil user = new BaseUtil( );
user.unboxAssociatedPartyRole( mapOfObjects );
List< Object > objs = user.associatedPartyRole;
assertIsA( objs, List.class );
Observe the results of running the experiment with the above refactor (pardon my French)…
[What The Reifiable Fuck#&*%$*!?]
EXPERIMENT SUCCESSFUL
;How can I use the value of a String variable as attribute or method name?
Want to do something like that:
class Person {
public String firstname;
}
String myAttributeName="firstname";
Person obj = new Person();
String firstNameOfObj = obj.{myAttributeName};
if you really want to do this, you could use reflection:
Person obj = new Person();
Method method = Person.class.getMethod("getFirstname");
String firstname = method.invoke(obj);
but as mentioned in the comments, you better use a map to hold attribute values:
class Person {
private Map<String,Object> attrs = new HashMap<>();
public void setAttribute(String attr, Object value)
{
attrs.put(attr,vaue);
}
public Object getAttribute(String attr)
{
attrs.get(attr);
}
}
Person person = new Person();
person.setAttribute("firstname","patrick");
String firstname = (String)person.getAttribute("firstname");
I am trying to compile this program. It works perfectly for 2 Strings(Name, phone number) But not for 3 Strings (Name, phone number and sex).
CODE (Not working code - 3 Strings (Name, phone number and sex))
import java.util.Map;
import java.util.TreeMap;
public class Ann {
String name, phone;
public Ann() {
}
public static void testMap() {
Map<String, String, String> theMap = new TreeMap<String, String,String>();
// new HashMap<K,V>(); could also be used
theMap.put("Roger M", "090-997-2918", "Male");
theMap.put("Jane M", "090-997-1987", "FeMale");
theMap.put("Stacy K", "090-997-9188", "FeMale");
theMap.put("Gary G", "201-119-8765", "Male");
theMap.put("Jane M", "090-233-0000", "FeMale");
System.out.println("Testing TreeMap and Map");
System.out.print("Stacy K has phone ");
System.out.print(theMap.get("Stacy K"));
System.out.print("\n");
System.out.print("Jane M has phone ");
System.out.print(theMap.get("Jane M"));
} // testMap()
public static void main(String[] args) {
testMap();
}
}
ERROR
wrong number of type arguments; required 2
wrong number of type arguments; required 2
WORKING CODE (For 2 Strings (Name, phonenumber))
import java.util.Map;
import java.util.TreeMap;
public class Ann {
String name, phone;
public Ann() {
}
public static void testMap() {
Map<String, String> theMap = new TreeMap<String, String>();
// new HashMap<K,V>(); could also be used
theMap.put("Roger M", "090-997-2918");
theMap.put("Jane M", "090-997-1987");
theMap.put("Stacy K", "090-997-9188");
theMap.put("Gary G", "201-119-8765");
theMap.put("Jane M", "090-233-0000");
System.out.println("Testing TreeMap and Map");
System.out.print("Stacy K has phone ");
System.out.print(theMap.get("Stacy K"));
System.out.print("\n");
System.out.print("Jane M has phone ");
System.out.print(theMap.get("Jane M"));
} // testMap()
public static void main(String[] args) {
testMap();
}
}
I want the code to work for about 5 attributes like name , phone, sex,age,address. If someone can help me compile the code at the top of the question, I can figure out the rest.
Thanks
You can't just add type parameters arbitrarily to generic types - they are defined with a certain number, and have to use that many (disregarding raw types). The type parameters have specific meanings for the implementation - how would the HashMap class know what you wanted to get out if you called map.get(name)?
You should encapsulate all the properties into a class (e.g. Person or Contact) and then create a Map<String, Person> to map from the name to the person. For example:
public enum Gender
{
FEMALE, MALE;
}
public final class Person
{
private final String name;
private final Gender gender;
private final Date dateOfBirth;
private final String address;
private final String telephone;
public Person(String name, Gender gender, Date dateOfBirth,
String address, String telephone)
{
// You probably want to put some validation in here
this.name = name;
this.gender = gender;
this.dateOfBirth = dateOfBirth;
this.address = address;
this.telephone = telephone;
}
public String getName()
{
return name;
}
// etc for the other properties
}
...
Map<String, Person> map = new HashMap<String, Person>();
Person jon = new Person("Jon", Gender.MALE, /* etc */);
map.put("Jon", jon);