Print out names from Mapped information - java

I've created a Map with ten people's details, I want to then print out the names.
At the moment I have to print out each one individually, I want to have about 200 people so I wanted to know if it would be possible to print the names out in a loop.
(simplified code)
public class PersonDetails implements Serializable {
private String name;
private int age;
...
public PersonDetails(int Age, String Name) {
name = Name;
age = Age;
...
}
}
public class Test implements Serializable {
Map<String, PersonDetails> people = new TreeMap<String, PersonDetails>();
public Test() {
//10 people added
for(int x = 0; x<10; x++){
addPerson();
}
PersonDetails p0 = people.get("0");
String p0name = p0.getName();
System.out.println(p0name);
PersonDetails p1 = people.get("1");
String p1name = p1.getName();
System.out.println(p1name);
PersonDetails p2 = people.get("2");
String p2name = p2.getName();
System.out.println(p2name);
PersonDetails p3 = people.get("3");
String p3name = p3.getName();
System.out.println(p3name);
...
(I would like to loop this)
}
OUTPUT:
Connor
Phil
Nick
Mike
Sarah
Tom
Jenny
Eric
Jerry
Dave
Is it possible to have the same output and loop these outputs? I have tried an ArrayLists but I can't get it to work with this problem, and I am not sure if it is possible.
Thanks

Yes it is:
for (PersonDetails p: people.keySet())
{
System.out.println(p.getName());
}
You should specify the types of the people member variable when declaring and initializing. I think the key is a String, given get("0"), so:
Map<String, PersonDetails> people = new TreeMap<String, PersonDetails>();
To use an ArrayList:
List<PersonDetails> people = new ArrayList<PersonDetails>();
people.add(new PersonDetails(42, "Martin"));
for (PersonDetails p: people)
{
System.out.println(p.getName());
}

Map<String, PersonDetails> people = new HashMap<String, PersonDetails>();
// add to the Map this way
people.put(person.getName(), person);
for (Person p : people.keySet()) {
System.out.print(String.format("%s ", p.getName());
}
System.out.println();
You get the idea.

Related

Find the Oldest person from an array of persons which contains their name and yearOfBirth Java

I need to implement a method which should take an array of persons, basically public String oldest (Person [] persons), and return the oldest one. The persons which will be inputed are the following:
new Person("Augusta Ada King, grevinna av Lovelace", 1815),
new Person("Muhammad ibn Musa al-Khwarizmi", 780),
new Person("Alan Turing", 1912),
new Person("Grace Hopper", 1906)
Below you can find my class Called Person. I've tried all different solutions with basic for-loop but I feel really lost and would appreciate any input or recommendation how I should write the method to find the oldest person.
class Person {
String name;
int yearOfBirth;
public Person(String name, int yearOfBirth) {
this.name = name;
this.yearOfBirth = yearOfBirth;
}
public int getAge() {
return getAge(java.time.LocalDate.now().getYear());
}
public int getAge(int year) {
return year - yearOfBirth;
}
#Override
public String toString() {
return String.format("%s %d", name, yearOfBirth);
}
public String oldest(Person [] persons){
}
You can try this:
Person oldest = Arrays.stream(persons).max(Comparator.comparing(Person::getAge)).get();
You need to iterate over your persons array and check which yearOfBirth is greater. You can implement your method like below:
public String oldest(Person[] persons) {
Person oldestPerson = persons[0];
for (Person person : persons) {
if (person.getYearOfBirth() < oldestPerson.getYearOfBirth()) {
oldestPerson = person;
}
}
return oldestPerson.getName();
}
This method should be static since it has nothing to do with an instance, and otherwise you have to call the method from an instance, which you probably don't want to do
Want should happen if you have more then one (oldest) person with the same age?
Why should the return be only the name and not a Person?
public static String oldest(Person[] persons) {
if (persons == null){
throw new NullPointerException("persons == null");
}
if (persons.length == 0) {
return null; //or throw same Exception depending on your handling
}
Person oldestPerson = persons[0];
for (Person person : persons) {
if (person.yearOfBirth < oldestPerson.yearOfBirth) {
oldestPerson = person;
}
}
return oldestPerson.name;
}
Since there could be the possibility of more than one oldest person I would do it as follows:
Here is some data with two oldest people (sort of)
Person[] people = {
new Person("Augusta Ada King, grevinna av Lovelace",
1815),
new Person("Muhammad ibn Musa al-Khwarizmi", 780),
new Person("Another oldest person", 780),
new Person("Alan Turing", 1912),
new Person("Grace Hopper", 1906) };
String s = Person.oldest(people);
System.out.println(s);
prints
Muhammad ibn Musa al-Khwarizmi
Another oldest person
first I would make the method static since it doesn't rely on instance fields but an array of instances.
I would use a map to facilitate holding the the names of the people using their age as the key. Use the Map.merge method to populate the map and handle duplicate ages
Now iterate thru the names and as you do so:
find the oldest age.
store the name as the value for that age.
if another is found of the same age, concatenate the name with a newline(\n) and update the current map value.
when finished, return the value for the computed oldest individual(s).
public static String oldest(Person[] persons) {
int oldest = 0;
Map<Integer, String> ages = new HashMap<>();
for (Person p : persons) {
int age = p.getAge();
oldest = Math.max(oldest, age);
ages.merge(age, p.name, (last, current)- > last + "\n" + current);
}
return ages.get(oldest);
}
In case you would rather return a List of names your method can look like this. The main differences are:
return a List<String> which contains the names
using a Map<Integer, List<String>> to contain the people based on age.
computeIfAbsent to initialize the maps value for that age one time and then add the name to the list.
public static List<String> oldest(Person[] persons) {
int oldest = 0;
Map<Integer, List<String>> ages = new HashMap<>();
for (Person p : persons) {
int age = p.getAge();
oldest = Math.max(oldest, age);
ages.computeIfAbsent(age, v->new ArrayList<>()).add(p.name);
}
return ages.get(oldest);
}
It would then be called like so
List<String> list = Person.oldest(people);
list.foreach(System.out::println); // to print - same as before.
My final recommendation is that you use Lists over Arrays as they have advantages, the main on (imho) being that they grow dynamically as you add more items.

How to inject function to method in java to reduce code repetition?

I'm currently learning Java and during my current project I need to print stats of students depending on the different courses: "Java", "DSA", "Databases", "Spring". I managed to get right data but as you can see below in code it is to many code repetition. Do you know maybe how to inject e.g. getJavaPoints() function when "Java" String is passed to printInfoAboutTopLearners(String courseName) method?
I don't use DB as repository currently as it is just learning project the repository is ArrayList<<Student>Student>.
The goal is to simplify code below:
public static void printInfoAboutTopLearners(String courseName) {
System.out.println(courseName);
System.out.println("id points completed");
ArrayList<Student> students = StudentRepository.getStudentRepository();
if (courseName.equals("Java")) {
students
.stream()
.sorted(Comparator.comparingInt(Student::getJavaPoints).reversed())
.filter(student -> student.getJavaPoints() != 0)
.forEach(student -> System.out.printf(Locale.US,
"%d %-8d %.1f%%\n",
student.getId(),
student.getJavaPoints(),
(double) student.getJavaPoints() * 100 / JAVA_POINTS));
}
if (courseName.equals("DSA")) {
students
.stream()
.sorted(Comparator.comparingInt(Student::getDsaPoints).reversed())
.filter(student -> student.getDsaPoints() != 0)
.forEach(student -> System.out.printf(Locale.US,
"%d %-8d %.1f%%\n",
student.getId(),
student.getDsaPoints(),
(double) student.getDsaPoints() * 100 / JAVA_POINTS));
}
if (courseName.equals("Databases")) {
students
.stream()
.sorted(Comparator.comparingInt(Student::getDbPoints).reversed())
.filter(student -> student.getDbPoints() != 0)
.forEach(student -> System.out.printf(Locale.US,
"%d %-8d %.1f%%\n",
student.getId(),
student.getDbPoints(),
(double) student.getDbPoints() * 100 / JAVA_POINTS));
}
if (courseName.equals("Spring")) {
students
.stream()
.sorted(Comparator.comparingInt(Student::getSpringPoints).reversed())
.filter(student -> student.getSpringPoints() != 0)
.forEach(student -> System.out.printf(Locale.US,
"%d %-8d %.1f%%\n",
student.getId(),
student.getSpringPoints(),
(double) student.getSpringPoints() * 100 / JAVA_POINTS));
}
}
Below Student class:
public class Student {
private final int ID_BASE = 10000;
private int id;
private String firstName;
private String lastName;
private String email;
private int javaPoints;
private int dsaPoints;
private int dbPoints;
private int springPoints;
public Student(String firstName, String lastName, String email) {
this.id = idGenerator();
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
this.javaPoints = 0;
this.dsaPoints = 0;
this.dbPoints = 0;
this.springPoints = 0;
}
public int idGenerator(){
return ID_BASE + StudentRepository.getSize();
} //Getters and Setters below
This is how I would model this:
public record Student(String firstName, String lastName, String studentID) {}
public record Course(String name, String id, Gradebook gradebook) {}
Student and Course objects should not (or need not) be directly related to each other. A student doesn't have a course. A student is ENROLLED in a course. Likewise, a course doesn't have a student. Attributes of a course should be limited to at least its name and its ID. A course could have other attributes like section ID, a boolean flag for availability, capacity (max number of seats), and description. I used Java records to eliminate boilerplate code. Also, these objects are immutable (and inherently thread-safe) because there is no reason to mutate them once they are created. I can still mutate the gradebook as you will see later on.
You then need to create classes that will complete the association between a student and a course. Something that comes to mind is Enrollment. In this class, you will have a list of courses containing a list of students enrolled in the particular courses. For this, I will use a class.
public class Enrollment {
private Map<Course, List<Student>>
// rest of class omitted
}
This is an entity a registrar's office would need. For this example, I indicated that Gradebook is an attribute of Course record. This means that Course object has-a Gradebook. A gradebook is a record of students and their particular grades. This is the class that, for your example, links or associates students and course grades.
public class Gradebook {
private Map<Student, List<Integer>> grades = new HashMap<>(); // mapping of student IDs and course grades
public List<Integer> getGrades(Student student) {
return grades.get(student);
}
public double getGPA(Student student) {
return getGrades(student).stream().mapToInt((x) -> x).summaryStatistics().getAverage();
}
public void addStudent(Student student) {
grades.put(student, new ArrayList<Integer>());
}
public void enterStudentGrade(Student student, int points) {
List<Integer> gradePoints = grades.get(student);
gradePoints.add(points);
grades.put(student, gradePoints);
}
public TopPerformer getTopPerformer() {
Map.Entry<Student, Double> topPerformer = grades.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
e-> e.getValue()
.stream()
.mapToInt(Integer::intValue)
.average()
.getAsDouble())).entrySet().stream().max((Entry<?, Double> e1, Entry<?, Double> e2) -> e1.getValue()
.compareTo(e2.getValue())).get();
String name = topPerformer.getKey().firstName() + " " + topPerformer.getKey().lastName();
return new TopPerformer(name, topPerformer.getValue());
}
public record TopPerformer(String name, double points) {}
}
It is worth noting that I didn't use a record for the gradebook because I want to be able to update (mutate) gradebooks after they are initially created.
Now that you have this framework in place, you can create a function to figure out (calculate) the top performers for each course. The question is: where do you put this method? IMO, since a Gradebook instance contains a record of students and their particular grades, you can have this class calculate the averages and return its top student. For simplicity, I added it to this class. I also believe it belongs here since the purpose of the Gradebook class is to manage student grades and calculating the top performer seems to be a derived functionality of this class. I also added the following main method to this class to test the code:
public static void main(String[] args) {
Student steve = new Student("Steve", "Jobs", "12345");
Student bill = new Student("Bill", "Gates", "67890");
Student john = new Student ("John", "Doe", "98765");
Student jane = new Student ("Jane", "Doe", "31245");
Course java = new Course("Java", "COMP1010", new Gradebook());
Course dsa = new Course("DSA", "COMP1020", new Gradebook());
dsa.gradebook().addStudent(john);
dsa.gradebook().addStudent(bill);
java.gradebook().addStudent(steve);
java.gradebook().addStudent(jane);
java.gradebook().enterStudentGrade(jane, 100);
java.gradebook().enterStudentGrade(jane, 95);
java.gradebook().enterStudentGrade(jane, 97);
java.gradebook().enterStudentGrade(steve, 96);
java.gradebook().enterStudentGrade(steve, 95);
java.gradebook().enterStudentGrade(steve, 97);
dsa.gradebook().enterStudentGrade(bill, 100);
dsa.gradebook().enterStudentGrade(bill, 95);
dsa.gradebook().enterStudentGrade(bill, 97);
dsa.gradebook().enterStudentGrade(john, 96);
dsa.gradebook().enterStudentGrade(john, 95);
dsa.gradebook().enterStudentGrade(john, 97);
System.out.println(java.gradebook().getTopPerformer());
System.out.println(dsa.gradebook().getTopPerformer());
}
There is no branching to figure out the course name and do the calculation for top performer. No matter what course it is (current or one that doesn't exist today), it calculates the top performer using the same function; which goes to the heart of your question which was eliminate duplicate code.
Executing the code above outputs the following:
TopPerformer[name=Jane Doe, points=97.33333333333333]
TopPerformer[name=Bill Gates, points=97.33333333333333]
The TopPerformer object doesn't need to be a record. It also doesn't need to be a class. You could simply modify the code slightly and return a map of the top student and his or her GPA.
You have a lot going on here, so without re-writing everything myself, I would suggest you embrace OOD and re-work your classes a bit. You already have a Student class. You should also had a Course class that contains a Map of students with their scores. You can then remove the various point fields from your student class. I'd also keep a CourseRepository class.
Course's have Students, Students don't have Courses.
Then your logic would go something like:
Get Course from CourseRepository using the courseName
Get Get the list of Students and their scores from the Course
Sort the list
Output the list
Though there are other improvement areas like class design etc. as suggested in other answers. Below code demonstrates injecting a function to a method to take out specifics out of the method while keeping only common logic inside. This method is then typically be called "higher order function" in functional programming paradigm terminology.
public static void printInfo(String courseName) {
switch (courseName) {
case "Java":
printInfoAboutTopLearners(Student::getJavaPoints);
break;
case "DSA":
printInfoAboutTopLearners(Student::getDsaPoints);
break;
case "Databases":
printInfoAboutTopLearners(Student::getDbPoints);
break;
case "Spring":
printInfoAboutTopLearners(Student::getSpringPoints);
}
}
public static void printInfoAboutTopLearners(Function<Student, Integer> valueGetter) {
System.out.println("id points completed");
List<Student> students = StudentRepository.getStudentRepository();
students
.stream()
.sorted(Comparator.comparing(valueGetter).reversed())
.filter(student -> valueGetter.apply(student) != 0)
.forEach(student -> System.out.printf(Locale.US,
"%d %-8d %.1f%%\n",
student.getId(),
valueGetter.apply(student),
(double) valueGetter.apply(student) * 100 / JAVA_POINTS));
}

Java issue reusing list to populate HashMap

I'm trying to set HashMap values in java whilst trying to reuse an arrayList. I hadn't worked with HashMaps before and am still get my head around this, so please bear with my limited knowledge.
Class Student:
public class Student {
private static int id = 0;
private String name;
private String surname;
private Map<Subject, List<Grade>> grades;
private List<Absence> absences;
}
grades setter:
public void setGrades(Map<Subject, List<Nota>> grades) {
this.grades = grades;
}
Class Subject:
public class Subject {
private String name;
}
Class Grade:
public class Grade {
private Term term;
private String letter;
}
Main program:
Subject subject = new Subject("History");
Map<Subject, List<Grade>> gradeMap = new HashMap<Subject, List<Grade>>();
List <Grade> grades = new ArrayList<Grade>();
grades.add(new Grade(Term.FIRST_TERM, 'A'));
grades.add(new Grade(Term.SECOND_TERM, 'F'));
grades.add(new Grade(Term.THIRD_TERM, 'B'));
gradeMap.put(subject, grades);
student1.setGrades(gradeMap);
Test printing the keys and values :
for(Map.Entry<Subject, List<Grade>> t :student1.getGrades().entrySet()){
Subject key = t.getKey();
for (Grade grade : t.getValue()) {
System.out.println(key.getName() + " - " + grade.getNumber());
}
}
But whenever i try to reutilize the ArrayList, since there are many students whose grades need setting, I lose all data in my student1 instance
grades.clear();
If I try printing to console now the output is empty:
for(Map.Entry<Subject, List<Grade>> t :student1.getGrades().entrySet()){
Subject key = t.getKey();
for (Grade grade : t.getValue()) {
System.out.println(key.getName() + " - " + grade.getNumber());
}
}
I would really appreciate any comments and suggestions on this, clearly I must be doing something wrong but I don't know what :D
Thanks in advance!
What you put in the map is a reference to that same list you still have. It didn't create a copy of it (as Java doesn't do such things), what you hold is still that same list that's in the map. If you want a new one you need to create it manually:
grades = new ArrayList<>();
And than the only reference to the one with Student1 record will indeed be in that map.
See Is Java "pass-by-reference" or "pass-by-value"? for more details.

please explain the List functionality during retrieving from this way

I have one List<Map<String, String>> first_list and Map<String, String> second_list.Whenever i try to retrieve the value from first_list to second list(map). I am getting the last value present in the first_list.
My code is:
Where a1,a2,a3 contain some column data from table
ex:
a1:{name,age,dob,..}
List<Map<String, String>> first_list={"a1","a2","a3"}// example
for (Map<String, String> second_list: firstList) {
String name = second_list.get("name");
}
please guide me.Thanks in advance
May be some well-compiled code will help to understand your code behavior
#Test
public void name() throws Exception {
Map<String,String> a1 = new HashMap<>();
Map<String,String> a2 = new HashMap<>();
Map<String,String> a3 = new HashMap<>();
a1.put("name", "a1");
a2.put("name", "a2");
a3.put("name", "a3");
List<Map<String, String>> first_list= Arrays.asList(a1,a2,a3);
for (Map<String, String> second_list: first_list) {
String name = second_list.get("name");
System.out.println("name = " + name);
}
}
Will output
name = a1
name = a2
name = a3
After reading your comment:
List> first_list={a1,a2,a3}, where a1,a2,a3 is not variable ,its object .Each one contain like this.. Ex:a1{name:apple,age:10;size:8},a2{name:grape,age:12;size:5}...
I dont understand why you have implement a complex scenario using List of Maps. And to be honest I do not understand your question.
But anyhow, I just want to guide you to another solution which I would prefer. Instead using a Map<String, String> just create an Object Fruits.
public class Fruit {
private String name;
private int age;
private int size;
public Fruit(String name, int age, int size) {
super();
this.name = name;
this.age = age;
this.size = size;
}
//getter and setter
I dont know from where you get the data inside your maps, but they have to come from anywhere. If you then just give those values in your new Fruit Object:
Fruit fruitApple = new Fruit("apple", 10, 8);
Fruit fruitGrape = new Fruit("grape", 12, 5);
You are able to put those in a List<Fruit> of Fruits.
List<Fruit> fruits = new ArrayList<>();
fruits.add(fruitApple);
fruits.add(fruitGrape);
And then you can iterate ofer the List.
public static void main(String[] args) {
Fruit fruitApple = new Fruit("apple", 10, 8);
Fruit fruitGrape = new Fruit("grape", 12, 5);
List<Fruit> fruits = new ArrayList<>();
fruits.add(fruitApple);
fruits.add(fruitGrape);
for(Fruit fruit : fruits){
System.out.println("name: " + fruit.getName());
}
}
output:
name: apple
name: grape
Its an easy implementation and easy to understand.

Comparing and deleting similar strings within a vector

JAVA: First off, Thanks so much for taking the time to look at my question; your help is EXTREMELY appreciated!
So, the problem is i have a Vector of Objects in Java and each object has a name(String). But, i have tons of objects that are repeated, and the ones that are repeated are always directly after the Object they repeat. Also the number of repeats ranges from 1-10(So frustrating) its completely random.
How would i go about deleting the repeats, I thought about comparing each objects name with the next in the vector and deleting all of the ones that match but that gave me tons of problems. Thank you SO much for your help in advance!
-Dylan
EDIT: Just to make sure you understand the kind of repetition i'm talking about ive added this.
Vector
---Object1(String name = "hi") --> Remove This one.
---Object2(String name = "hi")
---Object3(string name = "bob")
End Vector
Edit 2: add Code
public class Vector
{
public static void main(String args[])
{
Person person1 = new Person("jane");
Person person2 = new Person("jane");
Person person3 = new Person("bob");
Person person4 = new Person("shelly");
Vector<Person> vectorObject = new Vector<Person>
vectorObject.add(person1);
vectorObject.add(person2);
vectorObject.add(person3);
vectorObject.add(person4);
}
}
class Person
{
String name = null;
String bDay = null;
String color = null;
public Person(String name)
{
this.name = name;
}
}
It seems you should use a different data structure.
You may want to use a Set instead of a Vector. Sets do not contain duplicate elements. You've to override equals(Object) method.
Or use a Map with the name property as key value and store the corresponding Person object as value.
In both cases you prevent duplicates rather then deleting them afterwards.
Person person1 = new Person("jane");
Person person2 = new Person("jane");
Person person3 = new Person("bob");
Person person4 = new Person("shelly");
Map<String, Person> nameToPerson = new HashMap<>();
nameToPerson.add(person1.name, person1);
nameToPerson.add(person2.name, person2);
nameToPerson.add(person3.name, person3);
nameToPerson.add(person4.name, person4);
Collection<Person> noDuplicatesHere = map.values();
Well, I don't know which language are you using, so I will give you an algorithm in JavaScript:
var newvector=new Array();
var lastName;
for(var i=0;i<vector.length;i++){
if(!lastName || vector[i].name!=lastName){
lastName=vector[i].name;
newvector.push(vector[i]);
}
}
The problem is that this way a new vector is created, and if the original one is huge maybe you will have memory problems.
Here is your first problem:
class Person
{
String name = null;
String bDay = null;
String color = null;
public Person(String name)
{
name = this.name;
}
}
it should be:
class Person
{
String name = null;
String bDay = null;
String color = null;
public Person(String name)
{
this.name = name;
}
}
Here is more info on the this keyword:
http://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html
Your second problem is: I'm guessing you are trying to create a Vector, java.util.Vector to be exact. If you create a new instance of Vector inside of a class called vector, it will create a new instance of itself, not of java.util.Vector. You can either rename the class or you can just do:
java.util.Vector<Person> vector = new java.util.Vector<Person>();
if you want to compare 2 string values use:
String name = "John";
String name2 = "Joe";
if(name.equalsIgnoreCase(name2))
System.out.println("They match!");
You can also just use equals() if you want an exact match.
Hope this helped!

Categories

Resources