I have a object like this:
public class Person {
String name = "Any";
int age = 9001;
}
public String getName() {return this.name;}
public int getAge() {return this.age;}
Is there a way to print this data with predefined template as stated below?
Person firstOne = new Person();
print_throw_getters("Name: $name%s Age: $age%i",firstOne);
If no, I can convert an Object in the HashMap:
public HashMap getHashMap {
HashMap test = new HashMap<String,Object>();
test.put("name",this.getName());
test.put("age",this.getAge());
return test;
}
Is there a function to print values by keys in predefined template, like this?
print_throw_keys("Name: $name%s Age: $age%i",firstOne.getHashMap());
I do not want to use the indexes like printf("name: $1%s",string) since templates vary considerably, but the incoming object type is unique.
.
UPD: Thanks for the answers, but the idea is that there will be a file with a list of templates, which will be inserted into the data, for example:
String template1 = "Your selected Name is $name%s and selected age $age%i";
String template2 = "User: $name%s; Age: $age%i";
Accordingly, the objects themselves will contain a lot more getters.
You can use
MapFormat
Example:
String text = "Your selected Name is {name} and selected age {age}";
HashMap test = new HashMap();
test.put("name", "Mr. X");
test.put("age", "21");
System.out.println("Example: " + MapFormat.format(text, test));
Output:
Example: Your selected Name is Mr. X and selected age 21
There is no method in the Java API that can do such a thing, but it is easily implemented:
public static String buildMessage(Map<String, Object> data, String template) {
Pattern p = Pattern.compile("%([A-Za-z0-9_]+);");
Matcher m = p.matcher(template);
int offset = 0;
while(m.find()) {
int start = m.start(1) - 1 + offset;
int end = m.end(1) + 1 + offset;
String field = m.group(1);
String value = data.get(field).toString();
offset += -2 - field.length() + value.length();
template = template.substring(0, start) + value + template.substring(end);
}
return template;
}
and then you can use such method as follows:
String template = "-- this is my name: %name;, and my age: %age; --";
Map<String, Object> data = new HashMap<>();
data.put("name", "Robert Smith");
data.put("age", 20);
System.out.println(buildMessage(data, template));
This method is just a skeleton, if you want to use it for something more serious you'll have to improve this method to do the following:
validate that field names have only A-Z, a-z, 0-9, (or modify the regex)
validate cases where the template has fields not specified in the map
some other things that I'm missing right now
Just override the toString() method on your person object.
in person object
#Override
public String toString() {
return "Name:"+this.name+"age:"+this.age;
}
then use like System.out.printLn(person.toString());
Override toString() function to print from object state.
Example:
#Override
public String toString() {
return "Name: "+this.getName()+" Age: "+this.getAge()";
}
and to use it:
Person p = new Person();
...
System.out.println(p);
Couple of examples:
Ref1
Ref2
Sounds like an application of the Template Method design Pattern ...
Related
I have a Sorted Set in Java with an object with 2 strings, Name and Age. Name is unique.
Now I have the Name and I want to get the age based on the name.
I have my object:
SortedSet<Person> people;
That has 3 people inside: "John / 35", "James / 21" and "Maria /21"
Based on this, I want to check James age.
How can I do it? The only idea I have is just doing a for, but I guess it should be something easier.
I see two solutions there:
If there really are just this two properties, you could simply convert that to a map, where the name is the key and the age is the value, ( Map<String, Integer> ageMap). Then you can quickly get the age by using ageMap.get("James");.
Edit: To convert you can do this:
Map<String, Integer> ageMap = new HashMap<>();
for (Person p : people) {
ageMap.put(p.getName(), p.getAge());
}
int jamesAges = ageMap.get("James");
If you stay with the Set and the Person class, I would recommend using the streams:
Optional findFirst = set.stream().filter(e -> e.getName().equals("James")).findFirst();
if (findFirst.isPresent()) {
int age = findFirst.get().getAge();
}
Internally, this will probably still use some kind of for, but the real implementation might be a bit more optimized.
I would not use a set for this since you cannot easily retrieve values from a set. I would go with a map. You can populate the map anyway you like.
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "[" + name + ", " + age +"]";
}
}
Map<String, Person> people = new HashMap<>(Map.of("john", new Person("John",35),
"james", new Person("James", 21), "maria", new Person("Maria", 21)));
String name = "James";
Person person = people.get(name.toLowerCase());
System.out.println(person != null
? name + "'s age is "+ person.getAge()
: name + " not found");
prints
James's age is 21
I have a list of objects. Each object has a level attribute (int 1, 2 or 3), a City attribute ("New York", "Milan", "London") and generic String FamilyID.
The number of objects in the list is random.
I need to create a sub-list (or any other collection/data structure) which is made of the first objects in the list with any combination of Level and City (I don't care about the FamilyID).
Hope to make my request clearer with an example:
InitialList: [1, Milan, ID1][1, London, ID1][1, Milan, ID5][2, Lodon, ID1][2, London, ID6],[3, New York, ID2]
ExpectedOutput: [1, Milan, ID1][1, London, ID1][2, London, ID1][3, New York, ID2]
The output collection is a list of the first object with any combination of (level, City).
You could use streams with Collectors in this case. It will be something like this:
initialList.stream().collect(Collectors.toMap(City::key, city -> city, (f, s) -> f))
Full code example:
public class Stack {
public static void main(String[] args) {
List<City> initialList = List.of(new City(1, "Milan", "ID1"),
new City(1, "London", "ID1"),
new City(1, "Milan", "ID5"),
new City(2, "London", "ID1"),
new City(2, "London", "ID6"),
new City(3, "New York", "ID2"));
final ArrayList<City> expectedOutput = new ArrayList<>(initialList.stream().collect(Collectors.toMap(City::key, city -> city, (f, s) -> f)).values());
System.out.println(expectedOutput);
}
private static class City {
private final long level;
private final String city;
private final String family;
public City(long level, String city, String family) {
this.level = level;
this.city = city;
this.family = family;
}
public String key() {
return level + "_" + city;
}
#Override
public String toString() {
return new StringJoiner(", ", City.class.getSimpleName() + "[", "]")
.add("level=" + level)
.add("city='" + city + "'")
.add("family='" + family + "'")
.toString();
}
}
}
You need to write a customized comparator here based on the following pseudo logic:
if city is same among city lists, include only first city in the List(ignore second array)
if level is same and cities are different, include both level and city may be in descending order(of strings) only for the combination of level and city.
increment the level and for the incremented level, once-again do comparison of level and city same as you did in steps 1 and 2.
For String comparison, you can use the API, string1.compareTo(string2) and return whether the first letter is lesser, same or greater than based on negative, zero or positive value returned by compareTo method.
The New-List is then a Filtered List like the output you want.
i am creating student management simple java project using Maps collection where id is my key and the name,marks and mobile no. are values for the map. So how to print it in structured manner.
HashMap<Integer, LinkedHashSet<StudentCinstructor>> st = new HashMap<>();
LinkedHashSet<StudentCinstructor> st2 = new LinkedHashSet<>();
Scanner sc = new Scanner(System.in);
public void add() {
System.out.println("enter the name of the student");
String name = sc.nextLine();
System.out.println("enter the marks of the student");
double marks = sc.nextDouble();
System.out.println("enter the mobile number of the student");
long mobile_no = sc.nextLong();
st2.add(new StudentCinstructor(name, marks, mobile_no));
System.out.println("enter the unique id of the student");
int id = sc.nextInt();
st.put(id, st2);
with the custom class when i am trying to print it in main method its giving me an address with hashcode.
"HashmapDemo.MethodsForManagement#3d4eac69"
Two remarks :
1- When you try to print the object StudentCinstructor, if there is no dedicated toString() method, you will not get a well structured output. Therefore, what you need to do is write a toString() method for your class and then you can print to console.
Example :
public static String toString() {
return "Customize here + Put this method inside your class";
}
2- I don't see why you are using LinkedHashSet to store the StudentCinstructor objects and then storing this HashSet inside a map rather than creating the StudentCinstructor object and storing it in the Map directly if all students have a unique id.
Such as :
HashMap<Integer, StudentCinstructor> st = new HashMap<>();
Looking at your printed output "HashmapDemo.MethodsForManagement#3d4eac69", it seems you are printing an object of class HashmapDemo.MethodsForManagement. If you want to print an object of StudentCinstructor, you need to pass that object to the print method like System.out.println(student);.
And you need to override the toString() method in StudentCinstructor class. (i.e. put below code in StudentCinstructor class.)
(name, marks and mobile_no in below code are the fields in StudentCinstructor class.)
#Override
public String toString()
{
return "Name=" + name + ", Marks=" + marks + ", Mobile number=" + mobile_no;
}
I have a string that I am converting to an ArrayList with the intention of counting the number of particular values in the list.
My string may look like this :
String myString = "Living Room, Bedroom, Bedroom, Bedroom, Bathroom, Bathroom, Family Room."
I'm currently finding the number of occurences of unique values in this way:
Map<String, Long> count = Stream.of(myString.split(",")).collect(Collectors.groupingBy(w -> w, Collectors.counting()));
What's the best way to go about this so that I can print something along the lines of "My home includes 1 Living Room, 3 Bedrooms, 2 Bathrooms, and 1 Family Room."
For the moment, my solution is the following. I haven't yet accepted an answer, because I think the way I followed through is a little clunky:
String newDescription = "My Home includes ";
public String getDescription(){
String myDescription = home.getDescription();
Map<String, Long> count = Stream.of(myDescription.split(",")).collect(Collectors.groupingBy(w -> w, Collectors.counting()));
for (Map.Entry<String, Long> entry : count.entrySet()){
newDescription = newDescription + entry.getValue() + " " + entry.getKey() + "(s), ";
}
return newDescription;
}
I was previously going about it in this way, but another SO user suggested the change:
public ArrayList convertDescription(){
//getDescription is inherited from another class to provide a string like the myString example above
String myDescription = home.getDescription();
ArrayList<String>Map<String, listDescriptionLong> =count new= ArrayList<String>(ArraysStream.asListof(myDescription.split(",")));
int livingRoomOccurrences = Collections.frequencycollect(listDescription, "Living Room");
int bedroomOccurrences = CollectionsCollectors.frequencygroupingBy(listDescription, "Bedroom");
int bathroomOccurrencesw =-> Collections.frequency(listDescriptionw, "Bathroom");
int familyRoomOccurrences = CollectionsCollectors.frequencycounting(listDescription, "Family Room");
int kitchenOccurrences = Collections.frequency(listDescription, "Kitchen");
int walkInClosetOccurrences = Collections.frequency(listDescription, "Walk-in Closet");
//not sure what I should return to make it easy to override getDescription with the proper string formatting
return listDescription;
}
public String getDescription(){
return home.getDescription();
}
I suggest you get the frequency of all elements and extract what you need rather than scanning the collection again and again.
Map<String, Long> count = Stream.of(myString.split(","))
.collect(Collectors.groupingBy(w -> w, Collectors.counting());
I have a task to play with Java Collections framework. I need to obtain a users list from a database, and store it in a collection. (This is finished and users are stored in a HashSet). Each user is an instance of Person class described with name, surname, birth date, join date, and some other parameters which are not important now. Next I need to store the list in different collections (there's nowhere stated how many) providing functionality to sort them by:
- name only
- name, surname, birthdate
- join date
Ok, so to start with, my Person stores data as Strings only (should I change dates to Date ?). I've started implementing sorting with "by name, surname, birthdate", cause that's what I get after calling sort on list with Strings. Am I right ?
public List createListByName(Set set){
List ret = new ArrayList<String>();
String data = "";
for(Object p: set){
data = p + "\n";
ret.add(data);
}
Collections.sort(ret);
return ret;
}
But what with the rest ? Here's my Person :
class Person {
private String firstname;
private String lastname;
)..)
Person(String name, String surname, (..)){
firstname = name;
lastname = surname;
(..)
}
#Override
public String toString(){
return firstname + " " + lastname + " " + (..);
}
}
I wouldn't convert everything to strings to start with. I would implement Comparator<Person> and then sort a List<Person>:
public List<Person> createListByName(Set<Person> set){
List<Person> ret = new ArrayList<Person>(set);
Collections.sort(ret, new NameSurnameBirthComparator());
return ret;
}
The NameSurnameBirthComparator would implement Comparator<Person> and compare two people by first comparing their first names, then their surnames (if their first names are equal) then their birth dates (if their surnames are equal).
Something like this:
public int compare(Person p1, Person p2) {
// TODO: Consider null checks, and what to do :)
int firstNameResult = p1.getFirstName().compareTo(p2.getFirstName());
if (firstNameResult != 0) {
return firstNameResult;
}
int surnameResult = p1.getSurname().compareTo(p2.getSurname());
if (surnameResult != 0) {
return surnameResult;
}
return p1.getBirthDate().compareTo(p2.getBirthDate());
}
And yes, I would store the date of birth as a Date - or preferably as a LocalDate from JodaTime, as that's a much nicer library for date and time manipulation :)
so I should write multiple comprators on Person for each task ?
Given this is a homework task, then I would say that is the way you would start to learn about Comparators.
For interest sake only you can do this by creating a couple of resuable Comparators.
You can use the Bean Comparator to sort on individual properties.
Then you can use the Group Comparator to sort on multiple properties.