How to specify a class property from a string in java - java

So I am reading from a file with scanner it has the similar format:
title, name, age
Mr, Matthew, 20
mr, Paul, 30
miss, Anne, 24
CSV^
class person{
String name, title;
int age;
public crimeData(String csv){
String[]list = csv.split(",", -1);
name = list[0];
title = list[1];
age = list[2];
}
}
Console Program
Scanner input = new Scanner(System.in);
System.out.println("Please select what data you want to load:");
String selection = input.next();
int temp = 0;
for(int i=0; i< header.length; i++){
if(header[i].equals(selection)){
temp = i;
break;
}
}
temp will give us the index of the option specified so if it is 2 we will want to access the age property
When my console application runs I prompt them(the user) for the data that they want.
So they may enter "age" So I am lost on how I may take this "age" String and access the person object with it.
The ideal case for the program output should be: 20,30,24 going through each age and printing
I take their input so String input = scanner.nextLine();
Then I loop through my array of person objects to get the index of the input. Once I have this index I then want to access the property of person at the index. So like if my index was 1 I would want to access the property 'name'.
In javascript I could take the string and say person['age'] although java's a whole different story. I have looked into java's "reflection API" although it's a heavy learning curve.

I have looked into java's "reflection API" although it's a heavy learning curve.
Well, Reflection is the way to go. It's widely used in many frameworks.
But perhaps a simpler solution will fit your needs. Use a switch to decide which attribute to return, and encapsulate this in a method of the Person class:
class Person {
private String name, title;
private int age;
public loadData(String csv){
String[] list = csv.split(",");
name = list[0];
title = list[1];
age = Integer.parseInt(list[2]);
}
public Object attribute(String attribute) {
switch (attribute) {
case "name": return this.name;
case "title": return this.title;
case "age": return this.age;
default: throw new RuntimeException("Invalid attribute: " + attribute);
}
}
}
Encapsulating the switch inside the method is in line with OOP principles, since it hides how attributes are stored from other objects, only exposing an interface to query them. Reflection breaks all encapsulation.

Though in general I am not in favor of using Map for holding fields for an object, if the number of properties is large and could even potentially vary across CSV files (e.g., some file has the University a person attended, another does not), then using a Map to hold the properties might be appropriate.
In this case, one would define a simple Person class:
public class Person {
Map<String, String> props = new HashMap<>();
public void addProperty(String propertyName, String value) {
// could add error checking to ensure propertyName not null/emtpy
props.put(propertyName, value);
}
/**
* returns the value of the property; may return null
*/
public String getProperty(String propertyName) {
return props.get(propertyName);
}
}
If it is know that certain attributes/properties will always be loaded, then accessors such as getName() could be added:
public String getName() {
return props.get("name");
}
public int getAge() {
String age = props.get("age");
// or throw exception if missing
return (age != null ? Integer.parseInt(age) : -1);
}
Though note I would expect name to not be a single entry for most datasets, as there typically would be last name, first name, etc. Nonetheless, the pattern for a limited number of commonly expected values is the same. Also, you can adapt so that you could get integer values directly for certain well-known fields.
Then, when you parse the file, you keep the title row that has the attribute definitions. Then for each row that you subsequently read, you create a new Person object, and then add the properties in order.
List<Person> allPersons = new ArrayList<>();
while ( (line = READ_NEXT_LINE) ) {
// NOTE: this is not a safe way to handle CSV files; should really
// use a CSV reader as fields could have embedded commas
attrs[] = line.split(",");
Person p = new Person();
for (int i = 0; i < titleRow.length; ++i) {
p.addProperty(titleRow[i], attrs[i]);
}
allPersons.add(p);
}
You can then get a specific Person by Person myPerson = allPersons.get(index_of_person), and much akin to the way you would have used Javascript, you can do String val = myPerson.getProperty("age").
If you need to search by a given attribute, you can then stream/loop over the allPersons and check of equivalence based upon a given property.
// find all people of a given age
List<Person> peopleAge20 = allPersons.stream()
.filter(p -> p.getAge() == 20)
.collect(Collectors.toList());
System.out.println(peopleAge20);
// summary statics (average age) for all people
IntSummaryStatistics stats =
allPersons.stream().mapToInt(p -> p.getAge()).summaryStatistics();
System.out.printf("Average age: %f\n", stats.getAverage());
Note that this approach does break the idea of a Javabean, but that may or may not be an issue depending upon your requirements.

First thing, we should add a constructor to your Person class.
class Person {
public Person(String name, String title, int age) {
this.name = name;
this.title = title;
this.age = age;
}
}
Now while you read the input you can use a Map as follows. Here after reading each line, we create a Person object and then using that person's age we make an entry in the map with key as age and value as Person.
Map<Integer, Person> mapOfPeople = new HashMap<>();
while (input.hasNextLine()) {
String line[] = input.nextLine().split(",");
Perso person = new Perso(line[1], line[0], Integer.parseInt(line[2].trim()));
mapOfPeople.put(person.getAge(), person);
}
Now to fetch a particular Person by age just do
mapOfPeople.get(20);

Related

HashMap with multiple values get.key() error

import java.util.*;
class Student{
final String name;
final String gender;
public String number;
static Map<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
static ArrayList<String> nameandNumber = new ArrayList<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender,nameandNumber);
}
void getPersonByGender() {
String[] Liste = hm.get("Man").toArray(new String[0]);
for (int i = 0; i < Liste.length - 1; i += 2) {
System.out.println(Liste[i] + "\t<------>\t" + Liste[i + 1]);
}
}
}
hello guys i am creating a class and this class will return me 10 student information which I will give (according to the difference between men and women). When i try to use getPersonByGender's function this function gives me all students.
static ArrayList<String> nameandNumber = new ArrayList<>();
new is useful: Count the amount of times your code ever invokes new ArrayList. It's a fairly easy answer: Once. For your entire program.
If you only call new once, that means there is only one list. In the whole system.
No wonder then: This:
nameandNumber.add(this.number);
is called 10 times (because 10 students) for a single 'run' of your app. Thus, that one list you have must therefore have all these numbers added together - that's why you see all the data.
Your code is, unfortunately, layers of bad design decisions.
You can 'fix' the problem (but it'll still be fragile code that is hard to read), or you can 'fix' the design (which is more work).
Fix the problem
Instead of 1 list shared by all students which obviously can't work, you want to call new ArrayList for each student. Get rid of that static single arraylist, and instead make one every time:
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
var nameandNumber = new ArrayList<String>();
nameandNumber.add(this.name);
nameandNumber.add(this.number);
hm.put(this.gender, nameandNumber);
}
Now you call new ArrayList the right number of times throughout one run of your program, for example.
But you're still in trouble here - because you decided to use a List to represent a single idea (a student), you have confused yourself: A given gender maps to multiple students. Given that a single student is represented by a List<String>, multiple students would be a List<List<String>> and, oof, this is getting real complex, real fast.
We could plug away at fixing this further but let's take a step back and fix your design instead!
Fix the design
More generally, java's typing system is highly nominal: Types have names, and the more descriptive the name, the better.
Student is a far better, clearer name than List<String>. How is a reader of your code supposed to know that those List<String> objects specifically are intended to contain precisely 2 strings, the first of which is the student's name, the second of which is their student ID number? It doesn't say that anywhere. If you mess it up you get no compiler errors of any kind.
You have a type, right there, that properly describes that concept: Student!
So why not replace this bad code:
static Map<String, ArrayList<List<String>>> hm = new HashMap<String, ArrayList<List<String>>>();
With this much improved code:
static Map<String, List<Student>> genderMap = new HashMap<>();
It has all sorts of improvements:
It has a proper name. hm doesn't mean anything.
It uses <> to be shorter - you don't need to repeat that stuff.
It codes to the principle (List) instead of the concrete class.
It uses nominal types - this maps a gender string to a list of students. And the code reads the same way, that's good.
Putting it together:
class Student {
final String name;
final String gender;
final String number;
static Map<String, List<Student>> genderMap = new HashMap<>();
Student(String number, String name, String gender) {
this.name = name;
this.gender = gender;
this.number = number;
List<Student> studentsForThisGender = genderMap.get(gender);
if (studentsForThisGender == null) {
// There is no list yet; we need to make one.
genderMap.put(gender, studentsForThisGender = new ArrayList<>());
}
studentsForThisGender.add(this);
}
static void getPersonByGender() {
Student[] liste = genderMap.get("Man").toArray(new Student[0]);
for (Student student : liste) {
System.out.println(student.name + "\t<------>\t" + student.number);
}
}
}
Note:
getPersonByGender is now static - that's a thing you do to the concept of 'students' not any particula student.
Instead of this nebulous 'print list[0] - you just sorta have to know that's the name', we print student.name which documents itself.
We fixed the problem where you were list-confused.
If you get a little further along your java studies, that 'get the list of students for a given gender, and make a new list if neccessary' can be put more succintly. The last 4 lines can be reduced to:
genderMap.computeIfAbsent(gender, () -> new ArrayList<>()).add(this);
But I bet that syntax, and what is happening there, hasn't been covered yet in your java course.

How To reset appropriate parameters for the modification of existing values in java

I want to change these details of Benze car from user input values.
User will be able to change existing details from user input (ex: Name, Brand..).
How to reset the appropriate parameters to change the existing values?
Automobile Benze[] = new Automobile [5];
Benze[i]=new Automobile(); //Automobile is my class name
do {
System.out.println("01<Mercedize Benze");
String C = T.readLine();
int ch = Integer.valueOf(C).intValue();
switch(ch) {
case 01:
//Details of Benze car
Name = "Benze Class C";
Brand = "Benz";
Start = "On ";
Colour= "Grey";
Fuel = 3.5;
Km = 9;
Oil = 1.2;
water = 2;
for( i=0; i < 1; i++) {
Benze [i] = new Automobile(Name, Brand, Start, Colour, Fuel, Km, Oil, water);
Benze[i].show();
}
} while(exit == true);
Your code seems to be incomplete. Please show us the whole thing, including your automobile class.
Also, please always use meaningful variable names. We can never tell what something like C Is supposed to do.
As for your questions, if I understand correctly, you need to create setters for your properties, and then invoke them.
For example, a setter for a name would be
In the automobile class
public void setName(String newName){ this.name = newName; }
And that's it, you just invoke the method from elsewhere and pass it a name parameter. Do this for each property you want to modify, or create a method that changes all of them. The important part is understanding that you can change the properties by just using this.whatever = something
If you want to mutate ➡️ use setters:
class Automobile {
private String name;
...
public void setName(String name) {
this.name = name;
}
...
}
var auto = Benze[i];
auto.setName("new name");
, but better approach is creation new one
var old = Benze[i];
Benze[i] = new Automobile("new name", old.getBrand, ...);

How to read a text file into an array with private fields using mutators in Java

I have Googled this for a couple of days without much luck. I am trying to read a text file and use that information to populate the private fields of an array for a class object. I am new to Java and pretty new to programming in general.
What I've come up with for reading into the array seems really clunky and I feel there must be a better way, but I cannot find a good example for this particular kind of case case.
Creating a bunch of string variables was the only way I could get this to work. Perhaps main is a bad place to do this; perhaps Scanner is a poor choice here?
What better ways are there to implement this situation?
My text file that contains Strings and integers separated by whitespace on lines is similar to this:
Joe 2541 555-1212 345 1542 Type
Bob 8543 555-4488 554 1982 Type
... etc.
Here's my majority of my code thus far which is within main:
Scanner in = new Scanner(new FileReader("accounts.txt")); //filename to import
Accounts [] account = new Accounts [10];
int i = 0;
while(in.hasNext())
{
account[i] = new Accounts();
String name = in.next();
String acct_num = in.next();
String ph_num = in.next();
String ss_num = in.next();
int open_bal = in.nextInt();
String type = in.next();
account[i].setName(name);
account[i].setAcctNum(acct_num);
account[i].setPhoneNum(ph_num);
account[i].setSSNum(ss_num);
account[i].setOpenBal(open_bal);
account[i].setType(type);
i++;
}
class Accounts
{
public Accounts()
{
}
public Accounts(String n, String a_num, String ph_num,
String s_num, int open_bal, String a_type, double close_bal)
{
name = n;
account_number = a_num;
phone_number = ph_num;
ssn = s_num;
open_balance = open_bal;
type = a_type;
close_balance = close_bal;
}
public String getName()
{
return name;
}
public void setName(String field)
{
name = field;
}
public String getAcctNum()
{
return account_number;
}
public void setAcctNum(String field)
{
account_number = field;
}
//And so forth for the rest of the mutators and accessors
//Private fields
private String name;
private String account_number;
private String phone_number;
private String ssn;
private int open_balance;
private String type;
private double close_balance;
}
I believe you need to split each line in order to get the data contained in each line. You can use the split() of the string class which will return a string[]. Then you can go through each index of the string array and pass them to the mutator methods of the account class.
Something like this maybe.
while(in.hasNext())
{
// will take each line in the file and split at the spaces.
String line = in.next();
String[] temp = line.split(" ");
account[i].setName(temp[0]);
account[i].setAcctNum(temp[1]);
account[i].setPhoneNum(temp[2] + "-" + temp[3]);
account[i].setSSNum(temp[4]);
account[i].setOpenBal((int)temp[5]);
account[i].setType(temp[6]);
// will account for blank line between accounts.
in.next();
i++;
}
The phone number gets split into two separate indices so you have to rejoin the phone number by accounting for the first 3 digits being in one index and the last 4 being in the next.
Replacing Accounts[] with Set<Accounts> would be more flexible solution as you can process either 10 lines or 10000 accounts without code changes. In general: Consider Collections vs arrays
Using Scanner seems to be reasonable in this particular case, however take a look at the others ways of processing text (performance vs convenience): Scanner vs. StringTokenizer vs. String.Split
Setting up account parameters with mutators or constructor might not be the best choice: Effective Java: Consider a builder when faced with many constructor parameters

Java Input Output Logic

First of all, id like to thank this fourm, as I am finding myself quickly improving through all the material on this forum and all the help different members have been giving me. So this is just a big thank you for all of that. As for my question, I've been experimenting around with input out and wanted to see if this logic would work. I am trying to get the appropriate things in their appropriate array, and wanted to see if this logic would do it. Currently (and for a while) I wont be in a place where I can access any Virtual IDE effectively so all this was kinda done on the fly using notepad, word etc. *So don't be to hard on my syntax. What I am mostly concerned about is the logic (if it would work) and to a lesser mistake any major mistakes in code.*
Thanks alot.
So basically, the text file goes like this. Title, one line of space, then name, age and wage and the separator is the #. Then right below that, name, age and wage the separator bring # etc etc.
(pretend there was no line spaces between Bobby, Sandy, Roger, Eric and David..so pretend in the txt file they are right under each other, but there is a gap in between information and bobby.
Information
Bobby#24#5.75
Sandy #19#10.22
Roger #27#6.73
Eric#31#8.99
David#12#3.50**
Here is the logic i've come up with.
public class Practice {
public static void main(String[] args) throws Exception {
String Name [] = new String [5];
int Age [] = new int [5] ;
double Wage [] = new double [5];
String Blank [] = new String [5];
FileReader inputfile = new FileReader (new File(info.txt));
BufferedReader InputBuffer = new BufferedReader (inputfile);
String Title = InputBuffer.readline (); // to get the title
int count = 0;
while (InputBuffer.readline() = null) { // this while loop grabs the blank under the title
Blank [count] = count;
}
int i = 0;
while (InputBuffer.readline() !=null) {
String Getter = InputBuffer.readline (); // reads line
String splitup= Getter.split(#); // splits it
Name [i] = splitup[i]; // puts name in this array
Age [i] = splitup([i] + 1); // age in this array
Wage [i] = splitup([i] + 2); // wage in this array
}
InputBuffer.close();
}
}
Would this logic work for storing the title in the title String, the Blank line under the Blank Array, the name under the name array, age under the age array and the wage under the wage array??
Thanks alot.
P.S: Mostly concerned about the last while loop, I want to know if it will put the name in the name array, the age in the age array and the wage in the wage array.
First of all, you only need one while-loop. I don't understand why you have two, especially since the conditional in the first is nonsensical ( InputBuffer.readline() = null ).
Your loop would look something like this:
boolean isTitleParsed = false;
String title;
String line;
while ( (line = inputBuffer.readLine()) != null ) {
if (!isTitleParsed) {
// this is the title
title = line;
isTitleParsed = true;
} else if (line.isEmpty()) {
// this is a blank line; logic for dealing with blank lines here
...
} else {
// this is actual person data
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
String name = personData[0];
String age = personData[1];
String wage = personData[2];
...
}
...
}
}
Secondly, I think using arrays is entirely the wrong way to go. Like #AVD mentioned in his comment on the OP, List<T> and a POJO is probably a much better solution -- and much more extensible.
And finally: no, as you've written it, your second loop will not successfully save the name, age, and wage to the arrays. You never increment i and the syntax splitup([i] + 1) is just wrong. (You probably meant splitup[i+1].)
Using Arrays
If you're really stuck on using arrays to save your data, you'd have to do in something like this:
String[] names = new String[5];
String[] ages = new String[5];
String[] wages = new String[5];
...
int index = 0;
while ( (line = inputBuffer.readLine()) != null && index < 5) {
if (!isTitleParsed) {
...
} else if (line.isEmpty()) {
...
} else {
// this is actual person data
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
String name = personData[0];
String age = personData[1];
String wage = personData[2];
names[index] = name;
ages[index] = age;
wages[index] = wage;
index++;
} else {
System.err.println("Line " + line + " is malformed and was not saved.");
}
...
}
}
Notice that index is instantiated at 0, but is incremented every time we save something to the arrays. This way names[0] will hold the first name, names[1] will hold the second, and so on.
Notice also that we save a given record's name, age, and wage all at the same index. So we could expect names[0] to hold "Bobby", ages[0] to hold "24", and wages[0] to hold "5.75" -- all of which are related to the same record.
Finally, the condition in the while loop has been amended to be (line = inputBuffer.readLine()) != null && index < 5. This means we'll keep looping through the lines of the file until we either run out of lines (the file ends) or our index becomes greater than 5, which is the size at which we instantiated the array. This is one reason why arrays are such a bad structure to hold this data: you have to know exactly how many records you have in your file, and you may end up not filling them all the way (you allocated too much space) or not saving some records because you have no more room to store them.
Using POJOs
A much better way to save the data would be to use a POJO -- a Plain Old Java Object. This kind of object is pretty much a "data holder" object.
In your case, it would be something like this:
public class PersonData {
private String name;
private String wage;
private String age;
public PersonData() {
this(null, null, null);
}
public PersonData(String name, String wage, String age) {
this.name = name;
this.wage = wage;
this.age = age;
}
// ... getters and setters here
}
In your code, you'd replace your arrays with a List structure of PersonData objects:
List<PersonData> records = new ArrayList<PersonData>();
And in your while loop, you'd save into these objects instead of into the arrays:
// in the else in the while loop:
String[] data = line.split("#");
if (data != null && data.length == 3) {
PersonData record = new PersonData(data[0], data[1], data[2]);
records.add(record);
} else {
// error handling for malformed line
}
Now if you wanted to get data for a particular record, you'd just need to extract the PersonData object from your records list and query it:
// assuming the first record we scraped was "Bobby#24#5.75"
PersonData person = records.get(0);
person.getName(); // returns "Bobby"
person.getAge(); // returns 24
person.getWage(); // returns 5.75
Since we're using a List and not an array, we don't have to worry about knowing exactly how many records there are in the file, and we don't run the risk of losing information because we don't have anywhere to store it.
This way we can also know for certain that a name, age, and wage are all related to the same record, whereas before we were just hoping that, say, all records at index 0 in the arrays were related to the same person.
Also, if you add additional data to the records -- for example, name#age#wage#favorite food -- all you have to do is add a new field to the PersonData object and add a line in your parsing method to add that data to the object. If you were using arrays, you'd need to add a new array, and so on.
It's also much easier to create logic if, say, you have a row that only has a name or that missing a wage, and so on -- so that you're actually able to save the data in some meaningful fashion.
If you want to make good progress in Java or any OOP Language for that matter you should always approach a problem in a Object Oriented Manner.
For the problem at hand you should always consider a class to store the Person Info rather than using associative arrays.
class PersonInfo {
PersonInfo(String name,int age,float wage) {
this.name = name;
this.age = age;
this.wage = wage;
}
String name;
int age;
float wage;
}
The code is more or less the same from above...but it should give a List of PeopleInfo as output.
List<PersonInfo> peopleInfo = new ArrayList<String>();
boolean isTitleParsed = false;
while ( (line = inputBuffer.readLine()) != null ) {
if (!isTitleParsed) {
// this is the title
title = line;
isTitleParsed = true;
continue;
} else if (line.isEmpty()) {
// this is a blank line; logic for dealing with blank lines here
} else {
String[] personData = line.split("#");
if (personData != null && personData.length == 3) {
peopleInfo.add(new PersonInfo(personData[0],personData[1],personData[2]));
}
}

How can I access a field in an array, where the name of the field in stored in a variable?

I have an array of type Records[], which contains fields such as name, age, and score.
I have a method which will be will need to access one of these fields, but I won't know which.
I want to, for example, do the following:
String fieldToUse = "name";
System.out.println(myRecords[0].fieldToUse);
In this case I could switch fieldToUse to whichever field I wanted to find. This doesn't work, however - how do I do this?
Thanks in advance.
Edit: myRecords is of type Records.
This could be done using refection:
Field field = Record.class.getField(fieldToUse);
Object fieldValue = field.get(record);
Full exeample:
static class Record {
public String name;
public int age;
public Record(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) throws Exception {
Record[] records = new Record[2];
records[0] = new Record("David", 29);
records[1] = new Record("Andreas", 28);
System.out.println("Davids name: " + getField("name", records[0]));
System.out.println("Andreas age: " + getField("age", records[1]));
}
private static Object getField(String field, Record record) throws Exception {
return record.getClass().getField(field).get(record);
}
prints:
Davids name: David
Andreas age: 28
I think that what you need to do is impossible with Java.
With your structure, you should iterate over your Records[] and select the right one.
You may try using a HashMap<String, Record> where the String is fieldToUse.
This way you can just use something like hashmap.get(fieldToUse) to get the right Record
A possibility would be to change the interface of Records to have a getProperty() style method:
System.out.println(Records[0].getProperty(fieldToUse));
Internally, Records could use a Map implementation to store the values. Assuming name is a String, and age and score are Integers the Map would be Map<String, String>, keyed by the attribute name. Additionally, Records could provide methods for each attribute for use elsewhere that returned the appropriate type.
You can make a Map representation of type Record. With each field name as key.
Record
name: "YXZ"
age: 12
score: 657
Now store this in an array.
You can now access
Records[0].get("name");
This is the best I can think of in terms of Java.

Categories