Csv file with different number of data in each row - java

basically I'm trying to store data from a text file and perform some tasks with that data. So far I've parsed the data into different types (component, stock num, stock, and price), loaded the data into an object and put the object into an array list. I have a problem though as some of the rows in the txt file only have four data types while everything else has 5.
Therefore, I can only parse it to four data types instead of 5 because I get an ArrayIndexOutOfBounds Exception because diode doesn't have anything at index[4]. I know that I should use the length of the array but I'm not sure how. Also, how would I add together everything in a data type such as stock and get the total stock for all the components? Thanks.
try
{
List<Inventory> invItem = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader("inventory.txt"));
String fileRead = br.readLine();
while (fileRead != null)
{
String[] tokenize = fileRead.split(",");
String tempItem = tokenize[0];
String tempNumber = tokenize[1];
double tempStock = Double.parseDouble(tokenize[2]);
double tempPrice = Double.parseDouble(tokenize[3]);
double tempResist = Double.parseDouble(tokenize[4]);
Inventory tempObj = new Inventory(tempItem, tempNumber, tempPrice, tempStock, tempResist);
invItem.add(tempObj);
fileRead = br.readLine();
}

I'm only using some pseudo code here, because you haven't showed us what you have tried so far, to try to explain what you need to do:
for each line in CSV-file {
split line by ',' and store in array
check first item in array =>
if array[0].equals("diode") => create diode object from array of size 4
if array[0].equals("capacitor") => create capacitor object from array of size 5
...
store created object in a List<>
}
Both the diode class and the capacitor class extends a class that is used for the type of List<>
Now that your question has been updated with some code, I will give an example with real code. This example uses only an Inventory class, no Diode or Capacitor classes.
public class Inventory {
private String item;
private String number;
private double price;
private double stock;
private double resist;
public Inventory(String[] csvLine) {
if (csvLine.length > 0) item = csvLine[0];
if (csvLine.length > 1) number = csvLine[1];
if (csvLine.length > 2) stock = Double.parseDouble(csvLine[2]);
if (csvLine.length > 3) price = Double.parseDouble(csvLine[3]);
if (csvLine.length > 4) resist = Double.parseDouble(csvLine[4]);
}
// getters and setters
}
And some code using this class:
public static void main(String[] args) {
List<Inventory> invItem = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader("inventory.txt"));
String fileRead;
while ((fileRead = br.readLine()) != null) {
invItem.add(new Inventory(fileRead.split(",")));
}
br.close();
double totalStock = 0;
for (Inventory inv : invItem) {
totalStock += inv.getStock();
}
System.out.println(totalStock); // prints the total stock
}

A simple solution to avoid ArrayIndexOutOfBounds is... to check the length before attempting to read values. This code can help:
double tempResist = tokenize.length <= 4 ? 0 :
Double.parseDouble(tokenize[4]);
However, it is even better to ensure that all resistors have length 5; otherwise you would receive no warning when invalid resistors (missing a resistance value) are read:
double tempResist = "resistor".equals(tempItem) ? 0 :
Double.parseDouble(tokenize[4]);
And, even better, you would have separate Resistor and Capacitor subclasses of Item, and have an ItemParser class with a static Item parse(String[] tokens) method that parses the common bits, and creates an object of the correct subclass, passing the remaining tokens to its constructor. In their constructors, resistors would complain if they are missing values, while capacitors would complain if they get passed excess, unexpected values. This would also complain if a "chicken" is read, because there would be no subclass to parse its values.
Edit: it is better to have an ItemParser as a separate class than to place that functionality in the Item class directly, because this allows you to add new types if Item without touching the base Item base class.

Related

parsing String to Ints

We have an assignment/project where we create a parking lot with several different objects then typically each week we add/improve on it. This week we were given a .txt file with lines that look like this "Bicycle%4%2%ABC40%false" or "Auto%22%7%ABC21%false". Each % is used as a delimiter denoting a separate attribute (speed, passenger capacity, serial number, and boolean lock status for bikes) for the respective object (car or bicycle).
We need to read through the file and create a new object in an array with that line using split. What I'm having trouble with is taking the number which is currently a string, parsing it as an int or boolean, then adding that to the new Bicycle (or Auto). Just can't seem to get the syntax right.
Also wondering if my "if" logic is right for if it says Bicycle to start creating a bicycle?
Update:
I think I have the parsing issue figured out, but now it's saying that it can't find my attributes for bicycle... saying "Cannot find symbol" So close!
Here's my code:
public class Driver
{
private static Object myBicycle;
private static Object myAuto;
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws FileNotFoundException
{
Scanner scan = new Scanner(new File("Vehicles.txt"));
ArrayList<Object> myVehicles = new ArrayList<Object>();
while (scan.hasNext())
{
String line = scan.nextLine();
String [] data = line.split("%");
if (data[0].equals("Bicycle"))
{
speed = (Integer.parseInt(data[1]));
PassCap = (Integer.parseInt(data[2]));
serialNumber = (Integer.parseInt(data[3]));
locked = (Boolean.parseBoolean(data[4]));
brand = Brand.randomBrand();
Bicycle myBicycle = new Bicycle(speed, PassCap, serialNumber, locked, brand);
myVehicles.add(myBicycle);
}
I'm not sure what your implementation is, but you could try something like this:
public static void main(String[] args) throws FileNotFoundException
{
Scanner scan = new Scanner(new File("/home/buddha/MYDRIVE/exploring/java/src/Vehicles.txt"));
ArrayList<String> myVehicles = new ArrayList<String>();
while(scan.hasNext()) {
String line = scan.nextLine();
String[] data = line.split("%");
if (data[0].toLowerCase().equals("bicycle")) {
System.out.println(Integer.parseInt(data[1]));
System.out.println(Integer.parseInt(data[2]));
System.out.println(data[3]);
System.out.println(Boolean.parseBoolean(data[4]));
}
else if (data[0].toLowerCase().equals("auto")) {
System.out.println(Integer.parseInt(data[1]));
System.out.println(Integer.parseInt(data[2]));
System.out.println(data[3]);
System.out.println(Boolean.parseBoolean(data[4]));
}
}
}
and just like the setter functions, you can create getter functions and check if the values have been correctly stored.
So, this line:
String [] data = line.split("%");
...takes strings like this:
"Bicycle%4%2%ABC40%false" or "Auto%22%7%ABC21%false"
and turns them into arrays of strings like this:
data[0] : `Bicycle` or `Auto`
data[1] : `4` or `22`
data[2] : `2` or `7`
data[3] : `ABC40` or `ABC21`
data[4] : `false` or `false`
So now you just need to parse those strings into their int and bool equivalents:
speed = Integer.parseInt(data[1]);
passCap = Integer.parseInt(data[2]);
serialNumber = data[3]; // This is a string, right?
locked = Boolean.parseBoolean(data[4]);
If serialNumber is a numeric, then you probably only want the numeric part at the end (e.g. "21" vs "ABC21"):
serialNumber = Integer.parseInt(data[3].Substring(3));

Dynamic ArrayList fields

I have an assingment for school and I am having trouble with some ArrayLists. I have an input file which has one entry at every line. This entry has an integer and up to four strings. This input file is about locations that a film is filmed. The integer is the movieID in my case and the strings are the locations. However not every film has 4 locations which means that when my program tries to load the file it returns an error because it expects 5 fields at every row and this never happens because I have movies with 1 or 2 or the locations. I use a data loader class because I have to load several different files. My other files have a specific number of entries and fields at each row so loading those isn't a problem. The load process is done by adding the file into an array list and then creating the objects needed. I know that I need the program somehow to understand the empty fields and maybe handle them dynamically, for example a movie has 3 locations so the 4th field is empty, but I haven't figured it out yet. Any suggestions? Thank you!
This is my LocationsLoader class.
package dataLoader;
import java.util.ArrayList;
import dataModel.Locations;
public class LocationsLoader extends AbstractFileLoader<Locations>{
public int constructObjectFromRow(String[] tokens, ArrayList<Locations> locations) {
int movieID;
List<String> loc = new List();
movieID = Integer.parseInt(tokens[0]);
loc = tokens[]; // What goes here?
Locations l;
l = new Locations(movieID, loc);
locations.add(l);
System.out.println(l);
//System.out.println(locations.toString());
return 0;
}
}
And this is my Locations class:
package dataModel;
public class Locations {
private int movieID;
private List<String> loc;
public Locations(int otherMovieID, List<String> otherLocations) {
this.movieID = otherMovieID;
this.loc = otherLocations;
}
public int getMovieID() {
return movieID;
}
public void setMovieID(int id) {
this.movieID = id;
}
public String getLocations(int index) {
return loc.get(index);
}
}
}
You fill an array here
String[] tokens = new String[numFields];
for (int i = 0; i < numFields; i++) {
tokens[i] = tokenizer.nextToken();
}
but arrays are fixed length, there's really no reason to use them if you can have fewer values. Fill a list instead.
List<String> tokens = new ArrayList<>();
while (tokenizer.hasNextToken()) {
String token = tokenizer.nextToken().trim();
if (!token.isEmpty()) {
tokens.add(tokenizer.nextToken());
}
}
In fact, I'm not sure why you would need to give the reader the number of expected tokens at all.
But as Dodgy pointed out, you might as well use String#split:
String[] tokens = line.split(delimiter);
which will yield empty Strings as well, but you can just ignore those in your constructObjectFromRow function.

How to store data in an index in an ArrayList into Arrays in Java

I am new to Java and I am trying to do a calculation using values within a txt file. I have read the txt file, which has 3 tabs, each with three values
I have been able to read the file and get the columns as indices but cannot add the separate values into array. So I want three separate arrays
Read file method
public void read()
{
try
{
FileReader read = new FileReader(file);
String line = null;
while((line = FileReader.readLine()) != null)
{
a.add(line);
}
}
catch (FileNotFoundException e) {}
catch(IOException e) {}
}
Processing method
private void processor () {
for (String li : a)
{
String[] data = li.split("\\s");
Index = Double.parseDouble(data[0]);
Customers = Double.parseDouble(data[1]);
rateOfBuy = Double.parseDouble(data[2]);
}
}
I dont think you are thinking about your data structures correctly. If I were you I would think about this a little differently. To me it makes the most sense just to use a simple array. To handle the complexity of the three columns, I would create a new class called CustomerRate or something to that effect. I would then make the data into instance variables belonging to instances of that class. That way you could just have a simple array of CustomerRate objects and then access the data stored by each of those objects. This will probably be a lot simpler to deal with overall too.
I am not sure exactly what you are trying to accomplish but I'll do my best to help
You would create your new class to be something like this:
your new class:
//This is your new class
public class CustomerRate {
private int person;
private double rate;
//constructor
public CustomerRate(int person, double rate) {
this.person = person;
this.rate = rate;
}
//add appropriate getters and setters and whatever else you need
public double getRate() {
return rate;
}
}
Use the data your parse from your file to create new CustomerRate Objects. Create an array of your objects. Note that this is just an example with one entry with random numbers I'm going to use so you will have to get the loop and parse working:
//creating an example customer
CustomerRate customer1 = new CustomerRate(100, 0.5);
//create your collection to store your customer data that you will add/parse
List<CustomerRate> myList = new ArrayList<CustomerRate>();
//adds to list
myList.add(customer1);
//gets element at index and then grabs the rate
double exampleCustomerRate;
exampleCustomerRate = myList.get(0).getRate();
I coded this quickly so I may have made some mistake but I hope that gives you the general idea of what you should do.
You just need another ArrayList to store your rateOfBusiness. Something like this:
String file = "test.txt";
ArrayList<String> a = new ArrayList<String>();
ArrayList<Double> rateOfBusiness = new ArrayList<>(); //Define with your other fields
Then loop through your data and do the math while adding to the array
private void process () {
for (String li : a)
{
String[] data = li.split("\\t");
Index = Double.parseDouble(data[0]);
Customers = Double.parseDouble(data[1]);; //per year
rateOfBuy = Double.parseDouble(data[2]); //per year
rateOfBusiness.add(Customers*rateOfBuy); //Do math and store for that customer
}
}
Edit: Even though this solves your problem, I would look into learning some Object Oriented principles. IsaacShiffman (or Lvl 9 Oddish, or whatever his name is) has a start on how you would solve this going that direction. Makes your code a lot easier to follow and debug.

Java: How do i create objects in a static method and also call for methods from another class?

I am doing this Java assignment for hours and stuck with this tester class for very almost 5 hours.
In this assignment, I have created a Product class, a Money class, a LineItem class and an Inventory class. Now i need to create a test class to test the program by putting new lineitems into the inventory array.
In the tester class, I am trying to create a static method public static void addTestItems(Inventory theInventory) which suppose to add 4 items. For each item I will need to create a product object followed by a LineItem object to contain the newly created product. next i need to use a method from the inventory class to add the items into the array in the inventory class.
What i have tried too so far:
private static void addTestItems(Inventory theInventory)
{
Inventory[] _items;
Product product1 = new Product("Book","Objects first with Java"," An excellent introductory Java textbook");
Product product2 = new Product("CD","The dark side of the moon","The all-time classic Pink Floyd album");
Product product3 = new Product("DVD", "Transformers","Robots in disguise");
Product product4 = new Product("Laptop","Lenovo T42","A good yet affordabble laptop");
Money unitPrice1 = new Money(29,99);
Money unitPrice2 = new Money(4,99);
Money unitPrice3 = new Money(9,99);
Money unitPrice4 = new Money(450,0);
_items[0] = new LineItem(product1,5,unitPrice1);
_items[1] = new LineItem(product2,8,unitPrice2);
_items[2] = new LineItem(product3,200,unitPrice3);
_items[3] = new LineItem(product4,9,unitPrice4);
}
The current error is incompatible types- found LineItem but expected Inventory so i tried changing Inventory[] _items; to LineItem[] _items;. But the error was variable _items may not be initialise.
Sorry guys I am a real noob in Java, I tried searching on-line for ages but I do not quite understand most results. The only one i understand was http://forums.devshed.com/java-help-9/bluej-compiler-error-cannot-find-symbol-variable-object-688573.html but i tired putting into my context but failed. I also found lot of results but they had constructors and instance variables in them which my teacher specifically mentioned that I will not need them.
Wonder if expert could guide me along like let me know my mistakes. Thanks thanks.
The inventory class:
/**
* In the Inventory class, it is merely to create a list / array of product which allows the information from the linitem to be put with an index.
* For example, for the first product, we can use the inventory class to input it into the index 1. and he next product into index 2 and so on.
* It is suse to create an array and inputing the lineitem information into it.
*
* #author (your name)
* #version (a version number or a date)
*/
public class Inventory
{
// instance variables - replace the example below with your own
private LineItem[] _items;
private int _numItems;
/**
* Constructor for objects of class Inventory
*/
public Inventory()
{
// initialise instance variables
_items = new LineItem[1000];
_numItems = 0;
}
/**
* An example of a method - replace this comment with your own
*
* #param y a sample parameter for a method
* #return the sum of x and y
*/
public void addItem(LineItem item)
{
_items[_numItems]= item;
_numItems++;
}
public String toString()
{
String result="";
int i=0;
while (i < _numItems)
{
result = result + _items[i] + "/n";
i++;
}
return result;
}
public void print()
{
String myResult=this.toString();
System.out.println(myResult);
}
public Money getTotalValue()
{
int i=0;
Money total= new Money(0);
while (i<_items.length)
{
total = total.add(Money.NO_MONEY);
i++;
}
return total;
}
public LineItem getItem(String productName)
{
int i = 0;
LineItem itemDetails = null;
while (i<_items.length)
{
if (_items[i].equals(productName))
{
itemDetails= _items[i];
}
else
{
//do nothing
}
i++;
}
return itemDetails;
}
}
I have yet to comment on the methods yet but will do so once i understand it.
Your array is of type Inventory[] - but you're trying to assign references of type LineItem. You're also not initializing it. Change this:
Inventory[] _items;
to this:
LineItem[] _items = new LineItem[5];
And all should be well - although you're not using index 0 (which is why you need it to be size 5) and you're not doing anything with the array afterwards either...
Another alternative to using an array is to use a List:
List<LineItem> items = new ArrayList<LineItem>();
items.add(new LineItem(product1, 5, unitPrice1));
items.add(new LineItem(product2, 8, unitPrice2));
items.add(new LineItem(product3, 200, unitPrice3));
items.add(new LineItem(product4, 9, unitPrice4));
... next think about what you actually want to do with the items variable.
LineItem[] _items = new LineItem[4];
then the index starts from 0 not from 1,
_items[4]
will return indexoutofbounds error
A few things:
incompatible types- found LineItem but expected Inventory
is caused by the fact that your array is supposed to contain Inventory objects but you're assigning LineItems to it instead
variable _items may not be initialise
means that you have your _items object but you haven't initialized it to anything. You want to do
LineItem[] _items = new LineItem[4];
PS: If you want dynamically sized arrays, don't know how many line items you'll potentially load, etc etc use a vector or a collection or something along those lines.
Also,
_items[1] = new LineItem(product1,5,unitPrice1);
_items[2] = new LineItem(product2,8,unitPrice2);
_items[3] = new LineItem(product3,200,unitPrice3);
_items[4] = new LineItem(product4,9,unitPrice4);
In Java, array elements start with index 0 and not 1
_items
is a wonky variable name that makes your team mates sneeze in your coffee

incorrect Vector.size() returned

I'm implementing a Graph which holds "Book" objects as its nodes. The nodes are connected if the books share a keyword. The keywords for each book are held in a Vector within the Book class. To do this, I've created 3 classes.
1) Books 2) Vertex 3) Graph
The Vertex class holds the Book object and also has a Vector containing all the other Vertex objects (other books which share a keyword). In the Driver, I create the book, pass it to a Graph which then inserts it into a Vertex and finally the Vertex into a Vector named "bookGraph".
public final class Graph {
private Vector<Vertex> bookGraph = new Vector<Vertex>();
private int bookCounter = 0;
public Graph() {
}
public void addBook(Book bk) {
Vertex vtx = new Vertex(bk);
bookGraph.add(vtx);
bookCounter++;
System.out.println("Book #1 has " + bookGraph.get(0).getBook().getKeywords().size() + " keywords");
// addAdjVertices();
}
public void showKeywords() {
System.out.println("Book #1 is " + bookGraph.get(0).getBook().getKeywords().size() + " keywords");
}
The information from the books are read from a file in the Driver and inserted into a book object. I'm trying to make sure that this information is read in correctly and properly inserted into the Graph. My problem occurs when trying to get the size of the keyword Vector within the "showKeywords()" method in the Graph class. bookGraph.get(0).getBook().getKeywords().size() returns 0 when the exact same command in the addBook() method returns the correct size. I've implemented accessor methods such as getTitle() or getAuthor() in the Book class and those work correctly within the showKeywords() method. The keyword vector seems to be the only issue within the showKeywords() method. What am I doing wrong here?
Here is my Driver class....
boolean fileopen = false;
String title, author, keys;
long isbn_number;
Vector<String> keywords = new Vector<String>();
String filename = "books.txt";
String[] keywordTokens;
Scanner fin = null;
Scanner input = new Scanner (System.in);
Graph books = new Graph();
try {
fin = new Scanner (new FileReader(filename));
String fline;
fileopen = true;
System.out.println("Reading books.txt...");
while (fin.hasNextLine()) {
fline = fin.nextLine();
title = fline;
fline = fin.nextLine();
author = fline;
fline = fin.nextLine();
isbn_number = Long.parseLong(fline);
fline = fin.nextLine();
keywordTokens = fline.split(",");
for (int x = 0; x < keywordTokens.length; x++) {
keywords.add(keywordTokens[x]);
}
Book tempBook = new Book(title,author,isbn_number,keywords);
books.addBook(tempBook);
keywords.clear();
if (fin.hasNextLine()) fline = fin.nextLine();
}
books.showKeywords();
System.out.println("Ready.");
}
catch (FileNotFoundException e) {
System.out.println("FILE NOT FOUND!");
}
Looks to me like it should work - there's nothing obviously wrong (like accidentally using static variables). Can you provide a short but complete program which demonstrates the problem? The error is likely to be somewhere else - are you calling setKeywords(new Vector<String>()) somewhere, for example?
Any reason for using Vector rather than the more common ArrayList by the way? I would also suggest that setKeywords(String key) should probably be called addKeyword instead...
EDIT: Okay, now that you've posted the code it's obvious: you're only ever creating a single instance of Vector. You're then reusing that instance for every line, and clearing it at the end.
Just declare your keywords variable inside the loop, create a new instance on every iteration, and don't clear it afterwards.
To make your code as readable as possible (and avoid this sort of thing) I would suggest you declare every variable at the point of first use wherever possible, with the narrowest possible scope.
Could you try this snippet and check whether the error is still there:
public void test() {
Vector<String> keywords = new Vector<String>();
keywords.add("keyword");
Book bk = new Book("Author", "Title", 12345, keywords);
Graph bookGraph = new Graph();
bookGraph.addBook(bk);
bookGraph.showKeywords();
}
I think you got lost in your graph of objects :) I suggest to use unit tests to determine how your code should behave and to make sure it actually does behave the way you expect. The tests can build small examples and then check the various getters to see whether they return the correct results.
For what do you need the copy constructor Book(Book)? Perhaps you put copies of the books instead of the books itself into your collection?

Categories

Resources