Adding multiple authors to an ArrayList from user input - java

I have two classes and they both form ArrayLists. I have a menu option where the user needs to be able to add a book (title, copyright, ISBN, edition, and AUTHOR). This works entirely, but the problem is I need an option to add multiple authors and I cannot think of a way around this. Here is my working code below for adding a single author:
public void addBook(ArrayList<Book> books, ArrayList<Author> aut) {
Scanner scan = new Scanner(System.in);
System.out.println("Please enter the title:");
String title = scan.nextLine();
System.out.println("Please enter the copyright:");
int copy = scan.nextInt();
System.out.println("Please enter the edition:");
int ed = scan.nextInt();
System.out.println("Please enter the isbn:");
String isbn = scan.nextLine();
scan.nextLine();
System.out.println("Please enter the author: ");
String authors = scan.nextLine();
ArrayList<Author> aut = new ArrayList<>();
String firstName = authors.split(" ")[0];
String lastName = authors.split(" ")[1];
Author bookAuthor = new Author(firstName, lastName);
boolean foundAuthor = false;
for (int i = 0; i < aut.size(); i++) {
Author currentAuthor = aut.get(i);
if (currentAuthor.getFirst().equalsIgnoreCase(firstName)
&& currentAuthor.getLast().equalsIgnoreCase(lastName)) {
bookAuthor = currentAuthor;
foundAuthor = true;
break;
}
}
if (!foundAuthor) {
aut.add(bookAuthor);
}
au.add(bookAuthor);
books.add(new Book(title, copy, ed, isbn, au));
}
What is best approach to support the user entering multiple authors?

This will be fairly long, but hopefully this should help you.
First off, you should put a method in your Book class, that will prompt a user for authors, and add any of entered authors into the list for that object.
Here is what that class would look like along with a main that is just for testing purposes. I have excluded all of your other fields:
public class Book {
ArrayList<Author> authors = new ArrayList<>();
public Book() {
}
public Book(String title, int copyright, int edition, String isbn, ArrayList<Author> authors) {
/*
I left these fields out of this example on purpose.
this.title = title;
this.copyright = copyright;
this.edition = edition;
this.isbn = isbn; */
this.authors = authors;
}
public void promptForAuthors()
{
Scanner scan = new Scanner(System.in);
String response = "Y";
do {
System.out.println("Please enter the author. ");
String input = scan.nextLine();
String [] splitAuthors = input.split(" ");
Author author = new Author(splitAuthors[0], splitAuthors[1]);
if (!this.authors.contains(author)) {
this.authors.add(author);
}
System.out.println("Would you like you add an additional author? (Y or N) ");
response = scan.nextLine();
} while(response.equalsIgnoreCase("Y"));
}
public void printAuthors() {
for (Author a : this.authors) {
System.out.println(a.getFirstName() + " " + a.getLastName());
}
}
public static void main(String[] args) {
Book b = new Book(); //This will normally be the constructor that includes the ISBN, copyright, etc.
b.promptForAuthors();
b.printAuthors();
}
}
You can see from the main how you should use this in your addBooks code. Basically you create your Book out of all the other fields you used as input, then you can call this method on that object.
You will probably need to modify your current constructor so that it no longer requires the list for authors to be input when constructed, so you can use it afterward (you can just have both constructors available as well).
The promptForAuthors() will continue to prompt the user to enter in an Author until they hit anything other than Y or y.
This method will always ask for an author as least a single time, due to the use of a do-while loop. One of the main advantages of doing it in the Book class itself, is this method could be reused at any time to add more authors to the same book down the line if that is functionality eventually needed.
Additionally, the method will use .contains() to check if the Author already exists inside of the list, and only add the Author if it is a new Author. This requires an Override of .hashCode() and .equals() in your Author class which you can find the one I made below:
//IN THE AUTHOR CLASS PLACE THESE METHODS AND MODIFY FOR EXTRA FIELDS
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Author)) {
return false;
}
Author user = (Author) o;
return user.firstName.equals(this.firstName) &&
user.lastName.equals(this.lastName);
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + this.firstName.hashCode();
result = 31 * result + this.lastName.hashCode();
return result;
}
Note: I included a printAuthors() method just as an easy way to verify no extra duplicate Authors were added.
Example Run of adding Authors:
Please enter the author.
Joe Smith
Would you like you add an additional author? (Y or N)
y
Please enter the author.
Billy Bob
Would you like you add an additional author? (Y or N)
n
Joe Smith
Billy Bob

Try allowing the user to enter a special string to exit the input loop.
Change
String authors = scan.nextLine();
into a loop to populate a list of strings from input
ArrayList<String> authorNames = new ArrayList<>();
System.out.println("Please enter authors. Enter DONE when you are done.");
String nextLine = scan.nextLine();
while (!nextLine.equals("DONE")) {
authorNames.add(nextLine);
nextLine = scan.nextLine();
}
// This doesn't prevent the user from entering zero authors. Idk if you care about that.
alternatively, accept input of authors as only one line with some delimiter and use String.split on that delimiter later.

1 - create a list of lists.(List of Another list author).
2 - create an object of author list.
3 - perform all the action you want to perform on this author list.
4 - add author list to list of author list.
5 - To access author you need syntax like first_list_object.get(index).author_object_variable_or_member.
ArrayList<ArrayList<Author>> authorList = new ArrayList<>();
ArrayList<Author> au = new ArrayList<Author>();
String firstName = authors.split(" ")[0];
String lastName = authors.split(" ")[1];
Author bookAuthor = new Author(firstName, lastName);
au.add(bookAuthor);
authorList.add(au);

I think You're working with the wrong collection. You have a couple of author's. Each author could have written several books. So the right thing would be a Map<Author,ArrayList<Book>>.
It will make Your programming logic for addBook() easier, if You can use a map.

Related

Is there a way i can add multiple variables to one object?

So I've been given the method below and I'm not allowed to change it. What I need is it to create a couple of objects with the variables below but keeps coming up with an error that says "The constructor menu(int, String, String) is undefined." Am I doing something wrong?
import java.util.Scanner;
import java.util.*;
public class menu {
private static void addNewStudent()
{
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter the correct details below");
System.out.println("ID: ");
int userId = scanner.nextInt();
System.out.println("First name: ");
String userFirst = scanner.next();
System.out.println("Last name: ");
String userLast = scanner.next();
System.out.println("English assignment 1 mark: ");
int english1 = scanner.nextInt();
System.out.println("English assignment 2 mark: ");
int english2 = scanner.nextInt();
System.out.println("English assignment 3 mark: ");
int english3 = scanner.nextInt();
System.out.println("Math assignment 1 mark: ");
int math1 = scanner.nextInt();
System.out.println("Math assignment 2 mark: ");
int math2 = scanner.nextInt();;
System.out.println("Math assignment 3 mark: ");
int math3 = scanner.nextInt();
menu userStudentObj = new menu(userId, userFirst, userLast);
menu userEnglishObj = new menu(english1, english2, english3);
menu userMathObj = new menu(math1, math2, math3);
// Asks the user for the student information (ID, First, Last, Assignments)
// Then creates the appropriate objects and adds the students to the student list
I would expect to have created 3 new object that contain the user input variables within the objects if that makes sense.
The main problem is that you didn't specify any non default constructor, to create objects so compiler have no ideas what do you want to create if object is not specified for that. But there is another problem. Your object is designed in not the best way. So if you want to handle it correctly you could change your class design so you handle it in right way.
For example you could make it a bit different, like in this example (it's not perfect, it still can be more separate and better designed):
class User {
int userId;
String userFirstName;
String userSecondName;
private ArrayList<Integer> mathSums = new ArrayList<>();
private ArrayList<Integer> englishSums = new ArrayList<>();
User(int userId, String userFirstName, String userSecondName) {
this.userId = userId;
this.userFirstName = userFirstName;
this.userSecondName = userSecondName;
}
public void addMathSums(int mathSums) {
this.mathSums.add(mathSums);
}
public void addEnglishSums(int englishSums) {
this.englishSums.add(englishSums);
}
}
So by the help of this class you could change the way, how you define and set the values for your object and it would look like that:
1) You define constructor with arguments so you're able to use it for object creation:
User user = new User(userId, userFirst, userLast);
2) Then you can add sums into ArrayLists that are member variables (fields) in User class:
user.addMathSums(math1);
user.addMathSums(math2);
user.addMathSums(math3);
user.addEnglishSums(english1);
user.addEnglishSums(english2);
user.addEnglishSums(english3);
It could be performed in different ways using arrays, lists or other data structure, but you really need to pay attention for your class design. More time you spend on designing object less modifications you will need later.
3) You probably want to interact with this object so you need to get a reference for it somehow. You could change the method signature so you will "return" created object after you finish your input. So you need to make some changes:
On the method change the signature return type to User:
private static User addNewStudent() { ... }
Then add return for that and return created user:
private static User addNewStudent() {
/* code */
User user = new User(userId, userFirst, userLast);
/* add sums */
return user;
}
4) Use this method in your main():
public static void main(String[] args) {
User createdUser = User.addNewStudent();
}

JAVA. How to create method with variable from another scope in Java

I've started writing my own Java program during my freetime called the "Book A Ticket Machine", it's a Java console program with no GUI. It will Ask you for your FullName, FrequentFlyer ID, Age, then match you to your designated airline and flight number. While you travel your fuel will decrease and when it lands the fuel will fill up (I will create a fill method for this). I am having problems with calling a method from outside a scope.
Currently I have two files:
Flights.java --> Launching file. Linked with flightUserDatabase.
flightUserDatabase.java --> Contains all methods and class/blueprints all username, age, frequentFlyer, etc.
Code from Flights.java:
import java.io.Console;
public class Flights {
public static void main (String[] args) {
Console console = System.console();
//Book a Ticket Machine
//From Database otherwise Name not found on Database. Put Database in Another Class. Call it flightUserDatabase.
/* firstName: DONE
lastName: DONE
frequentFlyerID: Otherwise Invalid Number parseInt
Age: parseInt
FUEL MINUS AND FUEL ADD WHEN LAND.
*/
flightUserDatabase database = new flightUserDatabase();
System.out.println("Enter Creditials: ");
database.getDatabase();
String airline = console.readLine("ENTER YOUR AIRLINE: ");
String flightNumber = console.readLine("ENTER YOUR FLIGHT NUMBER: ");
String gate = "B7"; /* Declare data type String called "gate" */
//Next Version, Generate Random Number
System.out.println("This is an automated system. Please Wait...");
System.out.printf("%s %s is Departuring # Gate:%s \n", airline, flightNumber, gate); /* Use printf from java.io.Console library, then output Gate and Flight Number */
/* Notes: Data Types
> String name = "Ohm";
> int age = 42;
> double score = 95.5;
> char group = 'F';
*/
}
}
Code from flightUserDatabase.java:
import java.io.Console;
//Book a Ticket Machine
class flightUserDatabase {
Console console = System.console();
public String fullName;
public boolean getDatabase() {
boolean namesInDatabase;
do {
fullName = console.readLine("ENTER YOUR FULLNAME: ");
namesInDatabase = (fullName.equals("Ohm Passavudh") || fullName.equals("Matt"));
if (!namesInDatabase) {
console.printf("Sorry, that name is not in our database yet. \n");
}
if (namesInDatabase) {
console.printf("Welcome, Mr. %s \n", fullName);
}
} while(!namesInDatabase);
return namesInDatabase;
}
//If Ohm: FFID = 1234569
//If Matt: FFID = 246810
//FFID == FrequentFlyerID
/* Get name from inside scope fullName namesInDatabase variable */
public boolean frequentFlyerID()
I HAVE PROBLEMS HERE!!! I WANT TO SET Ohm's FFID to 1234569. But how to I determine if the user enters Ohm or Matt. I cannot access the String fullName from the other scope. I hope you understand me. If there is any misunderstanding I can clarify.
}
First, please, do work on your formatting, reading that code was awful.
You can create a class field and a getter in flightUserDatabase, so you can get the name after you've determined the name is in the database.
Or you can return it with getDatabase()
Like this...
public String getDatabase()
{
String fullName;
...
return fullName;
}
After all, you're not using that boolean.
... or this ...
class flightUserDatabase
{
private String fullName = "";
...
public String getName()
{
return this.fullName;
}
}
You have to initialize variable public String fullName; -> public String fullName = "";
Work on formating code
Name class begin from upper sign in your case FlightUserDatabase
Remember about encapsulation (private variable)

Java target/select class/object - confused (sorry for bad title)

What I have below is producing the desired results by print some employee details along with weekly / monthly wages as appropriate.
However I understand that I should not be inputting data in the constructor as I've done.
I need to prompt for a hours worked value only for "PartTimeEmployees", just not the way I've done it.
I've tested with For-Each loops, Enhanced For loops and using the instanceOf operator.
If I could get some guidance/hints or examples of how to accomplish what is currently being done in the constructor, but in the TestEmployee class instead that would be great.
Mostly I'm not sure how to even describe what I'm trying to achieve. This hinders Googling somewhat. (Help with a better title would also be great)
Thanks in advance.
public class TestEmployee
{
public static void main(String[] args)
{
int size;
Employee[] employees = new Employee[4];
employees[0] = new FullTimeEmployee("Jane", 26000);
employees[1] = new PartTimeEmployee("Jack");
employees[2] = new FullTimeEmployee("Lucy", 52000);
employees[3] = new PartTimeEmployee("Lenny");
for(int i = 0; i < employees.length; i++)
{
employees[i].print();
}
}
}
Class: PartTimeEmployee - Constructor:
public PartTimeEmployee(String thisName)
{
super(thisName);
System.out.println("Please enter the number of hours worked by " + thisName + ": ");
numHours = keyboard.nextInt();
setHours(numHours);
}
If I get your question, below might fit with your need -
First of all create generic Employee class -
class Employee {
private String name;
private int workingHours;
private final boolean IS_PART_TIME_EMP;
public Employee(String name, int workingHours) {
this.name = name;
this.workingHours = workingHours;
this.IS_PART_TIME_EMP = false;
}
public Employee(String name) {
this.name = name;
this.IS_PART_TIME_EMP = true;
}
public String getName() {
return name;
}
public int getWorkingHours() {
return workingHours;
}
public void setWorkingHours(int workingHours) {
this.workingHours = workingHours;
}
public boolean isPartTimeEmployee() {
return IS_PART_TIME_EMP;
}
}
Now you can use it as per your requirement.
Employee[] employees = new Employee[4];
employees[0] = new Employee("Jane", 26000);
employees[1] = new Employee("Jack");
employees[2] = new Employee("Lucy", 52000);
employees[3] = new Employee("Lenny");
Scanner sc = new Scanner(System.in);
for (Employee employee : employees) {
if(employee.isPartTimeEmployee()) {
System.out.println("Please enter working hours by " + employee.getName() + ": ");
int numHours = sc.nextInt();
employee.setWorkingHours(numHours);
}
}
Constructor is not meant for user input.Its main intention is to initialize object of that class.
Instead of doing that in constructor,you can try something like this
employees[1] = new PartTimeEmployee("Jack");
System.out.println("Please enter the number of hours worked by " + employees[1].getName()+ ": ");
numHours = keyboard.nextInt();
employees[1].setHours(numHours);
You most likely will have some logical main loop in your program, like
while(!quit) {
// 1. ask if you want to add part time or full time employee
// 2. ask proper questions
// 3. call correct constructor
}
Writing such small pseudo code algorithm should be self explanatory and get you going.
Step one: presentation of options available for user and reading user input.
Step two: performing actions depending on user input from step 1
Step three: final call to proper constructor depending on results from steps 1 and 2
If I understood your question correctly (which I'm really not sure of) you want to prompt for the employee data in the main method.
In that case I'd use a loop and query the following things:
name of the employee
does the employee work full time? (y/n)
if yes: what is the wage? (assume hours = whatever a full time employee works a day)
if no: how many hours? (and probably the hourly wage as well)
Then use that information to construct an Employee object (I don't see any need for the subclasses here).

Java (eclipse) public variables not saving values

I am running into some issues with my Java program. We have to create a library, which contains the title(halo, lotr) , the format (xbox, dvd etc), the date loaned (if it is ever loaned), and the person it is loaned to (if it is ever loaned).
I am not complete with my code, however I am testing it out as I go along instead of just compiling the entire finished code after 5 hours of coding. I am running into a problem. Whenever I set a public string variable to a value, it saves in the method I declared it in, but it will display "null" when system.out.print'd in other methods.
heres my code. First class is Library.
package p1;
import java.util.Scanner;
public class Library {
// \/ FIELDS
private String[] mediaItemTitle = new String[100];
public String[] mediaItemFormat = new String[100];
public String[] mediaItemLoanedTo = new String[100];
public String[] mediaItemOnLoan = new String[100];
public String[] mediaItemDateLoaned = new String[100];
public String today = "3/9/2015";
public int numberOfItems;
// /\ FIELDS
// \/ METHODS
public static void main(String[] brad){
Scanner input = new Scanner(System.in);
MediaItem main;
main = new MediaItem();
String title;
String format;
String date;
String name;
for ( int i = 0; i != 5; ){
i = displayMenu();
if (i == 1){
System.out.println("What is the title? ");
title = input.nextLine();
System.out.println("What is the format? ");
format = input.nextLine();
main.MediaItem(title,format);
}else if (i == 2){
System.out.println("Which Item (Enter the title? ");
title = input.nextLine();
System.out.println("Who are you loaning it to? ");
name = input.nextLine();
System.out.println("When did you loan it to them? ");
date = input.nextLine();
}else if (i == 3){
main.MediaItem();
}else if (i == 4){
System.out.print("Which item? (enter the title) ");
title = input.nextLine();
main.markReturned(title);
}else if (i == 5){ // DONE
System.out.print("Goodbye!");
break;
}
}
}
public static int displayMenu(){ // DONE
Scanner input = new Scanner(System.in);
int choice = 0;
System.out.println("1. Add new item");
System.out.println("2. Mark an item as on loan");
System.out.println("3. List all items");
System.out.println("4. Mark an item as returned");
System.out.println("5. Quit");
choice = input.nextInt();
return choice;
}
public void addNewItem(String title, String format){
this.mediaItemTitle[numberOfItems] = title;
this.mediaItemFormat[numberOfItems] = format;
System.out.print("TEST: " + mediaItemTitle[numberOfItems]);
}
public void incrementNumberOfItems(){
numberOfItems++;
}
public void listAllItems(){
for (int i = 0; i < numberOfItems; i++){
System.out.print(mediaItemTitle[i])
}
}
Here is the second part of code, my second class MediaItem
package p1;
public class MediaItem {
// \/ METHODS
public void MediaItem(){
Library list;
list = new Library();
list.listAllItems();
}
public void MediaItem(String title, String format){
Library call;
call = new Library();
call.addNewItem(title, format);
call.incrementNumberOfItems();
}
// /\ METHODS
}
This is driving me insane. I would love to just have me public variables save their value between methods but its not happening. the console (when 3 is chosen from displayMenu)
0
null
which means numberOfItems and mediaItemTitle[i] are read to be 0, and null. Which I dont understand, because I declared them earlier in the program!!!
I dont understand what Im doing wrong. please help me! Thank you!!
Your main mistake is that you are creating a new instance of Library inside your MediaItem method. That Library object will only live in the scope of MediaItem method. Plus Library is your main static class.
Your design is all wrong in my opinion. It looks like you are learning you way to Java or OOP, which is perfectly fine to have these mistakes.
Separate your data from your main class, create new classes just for your data. Have a look at java POJO (Plain Old Java Objects), like here
For example:
String title;
String format;
String date;
String name;
Should be in a new object, a POJO. Something like:
public class MyDataPOJO {
private String title;
private String format;
private String date;
private String name;
public MyDataPOJO(String title, String format, String date, String name) {
this.title = title;
this.format = format;
this.date = date;
this.name = name;
}
public String getTitle() {return title;}
public String getFormat() {return formate;}
// And the rest of the getter methods for date and name
}
In you Library class you may only need to hold your logic. But even that can be re-factored to another class.
On a side note, please check the java naming convention. Here is a guideline: link. In other words, start you methods name with lower case.
Example, your public void MediaItem(){/** something*/} should be public void mediaItem(){/** something*/ }
Follow the answer above and treat this as a comment, since the persons answer is correct and my statement isn't regarding your primary problem.
In your for-loop, I think you should add another else if statement. If the user enters a number that is not 1-5, they should receive an error. So maybe something like
else if (i < 1 || i > 5)
System.out.println("Error: Enter a choice 1-5\n");
Also, I think you may have forgotten a } to end your listAllItems() method.
But as I was saying, the answer to your real problem has already been handled, so give them the check mark. This is just a minor UI error I noticed.

Is it possible to return or somehow access the data from "courses" from this method?

I'm working on my first programming assignment for Java and I had another question. I put a Course[] inside of Student[] but now seem to be encountering a NullPointerException error and I can't figure out why.
public Student[] analyzeData() {
Scanner inputStream = null;
try {
inputStream = new Scanner(new FileInputStream("Programming Assignment 1 Data.txt"));
} catch (FileNotFoundException e) {
System.out.println("File Programming Assignment 1 Data.txt could not be found or opened.");
System.exit(0);
}
int numberOfStudents = inputStream.nextInt();
int tuitionPerHour = inputStream.nextInt();
Student[] students = new Student[numberOfStudents];
for (int i = 0; i < numberOfStudents; i++) {
String firstName = inputStream.next();
String lastName = inputStream.next();
int studentID = inputStream.nextInt();
String isTuitionPaid = inputStream.next();
int numberOfCourses = inputStream.nextInt();
Course[] courses = new Course[numberOfCourses];
for (i = 0; i < numberOfCourses; i++) {
String courseName = inputStream.next();
String courseNumber = inputStream.next();
int creditHours = inputStream.nextInt();
String grade = inputStream.next();
Course currentCourse = new Course(courseName, courseNumber, creditHours, grade);
courses[i] = currentCourse;
}
Student currentStudent = new Student(firstName, lastName, studentID, isTuitionPaid, numberOfCourses, courses);
students[i] = currentStudent;
}
return students;
}
The formatting for the input file is:
3 345
Lisa Miller 890238 Y 2
Mathematics MTH345 4 A
Physics PHY357 3 B
Bill Wilton 798324 N 2
English ENG378 3 B
Philosophy PHL534 3 A
Where courses has information about the courses, and students has information about the students.
The natural object mapping for your file would be a list of Student objects, each containing a list of Course objects. The courses array should be stored inside the student object.
Hmm, the way I would do this is have a data structure that represents the parsed file in memory. So basically it would look something like this:
public class RegistrationFile{
private Students[] students;
private Courses[] courses;
public void loadFromFile(File f){
//do your logic to parse the file
//and store the results in the appropriate data members
}
public Students[] getStudents(){
return students;
}
public Courses[] getCourses(){
return courses;
}
}
From your other code, you would then create an instance of a registration file object and call loadFromFile() to load data before you attempt to access anything.
I would add an array (or better yet, ArrayList or a Map) to Student that contains the classes the student is taking.
With what you're doing, how are you determining which classes go with which students?
Try adding the following instance variable
private List<Course> courses;
to Student, then implement the following methods to add to the List or return the whole thing.
void addCourse(Course c) { /*code here*/ };
List getCourses() {/* code here */} ;
And reading files can be a real pain, what you have is ok, at least for now. Your loop where Course is instantiated would include a call student.addCourse(course). Then you'll be golden.
Be advised this is a high level overview so there could be some learnage in here for you. Just post back and we'll help.
In addition to setting up the right objects to represent students/courses (as #ChrisThompson has), you'll want to be able to read in any number of records for student/courses right?
You'll want to parse the file accordingly. For example when you hit a blank line you know you're moving on to the next record (except for first record). Also take advantage of the fact that the first line will be the student (or if it has 5 tokens) and the subsequent lines are her courses (or if the line has 4 tokens).
This unit should comprise one iteration in the loop over the entire input file. Create a given student object and his course objects one iteration of the loop.

Categories

Resources