Read data from a CSV file, compare and display in Java - java

I have 3 csv files as follows:
Author file with columns: emailID | firstname | lastname
Books file with columns: title | isbn | author_email | description
Magazine with file Columns: title | isbn | author_email | releasedate
I need to display:
Based on the ISBN display all books and magazines
All books and magazines by an author
All books and magazines by title
I am using BufferedReader as of now:
String csvFileToRead = "csvFiles/authors.csv";
BufferedReader br = null;
String line = "";
String splitBy = ";";
try {
br = new BufferedReader(new FileReader(csvFileToRead));
while ((line = br.readLine()) != null) {
String[] book = line.split(splitBy);
System.out.println("BOOKS [Email Address= " + book[0] + " , firstname="
+ book[1] + " , lastname=" + book[2] + "]");
}
}
I am confused about how to proceed with multiple files. Here are the approaches that i have considered:
Change
String csvFileToRead
to a String array
String[] csvFileToRead = {"data/authors.csv", "data/books.csv", "data/magazines.csv"};
Then pass each index each time to a method returning all rows but getting stuck with DS to use. I think ArrayList won't suffice since i need separated data.
Should I use a 2D array?
Do I need to read and store data in a DS in order to achieve the goal?
Should I make 3 different classes with getters and setters for authors, book and magazine?
What is the ideal way to do this? Please suggest.

I would create 3 List<String[]> each containing the matrix from one CSV file with a method like:
public List<string[]> csvToList(String csvFileToRead){
BufferedReader br = null;
String line = "";
String splitBy = ";";
try {
br = new BufferedReader(new FileReader(csvFileToRead));
List<string[]> list_csv = new ArrayList<string[]>();
while ((line = br.readLine()) != null) {
String[] book = line.split(splitBy);
authors.add(book);
}
catch(Exception ex) {}
return list_csv;
}
And after that you can use it as :
List<String[]> authors = csvToList("csvFiles/authors.csv");
List<String[]> books = csvToList("csvFiles/books.csv");
List<String[]> magazine = csvToList("csvFiles/magazine.csv");
You can now print all what you want, for example the first case : Based on the ISBN display all books and magazines :
public void printISBN(string ISBN){
for(String[] s_tab in books)
if(s_tab[1].equals(ISBN))
System.out.println(s_tab[0]);
for(String[] s_tab in magazine)
if(s_tab[1].equals(ISBN))
System.out.println(s_tab[0]);
}
Hope I helped.
Edit
You could also create 3 classes for each files and do the same with Lists of thoses classes : List<Authors> List<Books> List<Magazine> but it is not necessary if you just have to answer those 3 questions.

One simple solution is to create three classes corresponding to your entities:
public class Author {
private String email;
private String firstName;
private String lastName;
// ...
}
public class Book {
private String title;
private String isbn;
private String authorEmail;
private String description;
// ...
}
public class Magazine {
private String title;
private String isbn;
private String authorEmail;
private Date releaseDate;
// ...
}
In your main code, when reading your CSV files you can fill a HashMap that maps an ISBN to a book, and a similar one for magazines, which you can use to retrieve a book or magazine by ISBN:
Map<String, Book> isbnBookMap = new HashMap<String, Book>();
Book book = isbnBookMap.get(isbn);
Map<String, Magazime> isbnMagazineMap = new HashMap<String, Magazime>();
Magazime magazine = isbnMagazineMap.get(isbn);
Similarly for getting a book or magazine by title.
As for getting books by author, you would need to map the author mail address to a List of Books since an author can have multiple books:
Map<String, List<Book>> authorBookMap = new HashMap<String, List<Book>>();
List<Book> books = authorBookMap.get(authorAddress);

You could use a Java CSV API
like OpenCSV

Related

Create ArrayList from Postgresql query

I have following class:
public class Person {
private String firstName;
private String lastName;
private List<String> habits;
}
I have Postgres query which returns me following result using inner join from 2 tables
name | lastname| habits
--------------------------------------
John | Smith | ["walking", "eating"]
I am trying to write RowMapper as following:
private final RowMapper<Person> rowMapper = (rs, rowNum) -> {
String firstName = rs.getString("name");
String lastName = rs.getString("lastName");
}
I am not sure how to create List habits from "habits column in my result table"
Please advise
Thanks
Found an answer
The code below is working fine
String[] habits;
try {
habits = objectMapper.readValue(rs.getString("habits"), String[].class);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
List<String> habitsList = Arrays.stream(habits).toList();
I have tried it and working fine. You can try the below code what I have understand from your description.
String firstName = rs.getString("name");
String lastName = rs.getString("lastName");
List<String> list = rs.getObject("habits");
if you get compilation error, it will work definitly
List stringList = new ArrayList;
List objectList = stringList;// this does compile only if List where subtypes of List
objectList.add(new Object());
String s = stringList.get(0);
it should work.
Let me know if you facing still issue

Java: Using .txt files to assign specific indexes of each line to an arraylist

My .txt file is
code1,description1,price1
code2,description2,price2
etc.
Using:
ArrayList<String> items = new ArrayList<String>();
String description;
File fn = new File("file.txt");
String[] astring = new String[4];
try{
Scanner readFile = new Scanner(fn);
Scanner as = new Scanner(System.in);
while (readFile.hasNext()){
astring = readFile.nextLine().split(",");
String code = astring[0];
items.add(code);
description = astring[1];
}
}catch(FileNotFoundException){
//
}
for(String things: items){
System.out.println("The code is: " + things + "The description is " + description);
}
My output prints out
code1 description1
code2 description1
code3 description1
I'm trying to figure out how to make the description update as the code's do. e.g.
code1 description1
code2 description2
code3 description3
If this question has been asked already, I apologize. I couldn't find out how to do it by searching around but if there's a reference to figure this out I'll close this down and go there. Thanks in advance!
The problem is with your logic. You are storing only astring[0] to the items ArrayList and overwriting the value of description each time. As a result on last value read is stored in description which you are printing in the loop.
I prefer creating a custom class as follows. (Just for the sake of demo otherwise you would declare your fields as private and provide getters and setters)
class MyObject {
public String code;
public String description;
public String price;
}
now instead of creating ArrayList of Strings you will create ArrayList of MyObject as follows
ArrayList<MyObject> items = new ArrayList<MyObject>();
now create a new instance of MyObject each time you read a line , populate its fields with the values from astring as follows
ArrayList<MyObject> items = new ArrayList<MyObject>();
File fn = new File("test.txt");
String[] astring = new String[4];
try {
Scanner readFile = new Scanner(fn);
Scanner as = new Scanner(System.in);
MyObject myObject;
while (readFile.hasNext()) {
astring = readFile.nextLine().split(",");
myObject = new MyObject();
myObject.code = astring[0];
myObject.description = astring[1];
myObject.price = astring[2];
items.add(myObject);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
and then finally print it using the same foreach loop as follows
for (MyObject item : items) {
System.out.println("The code is: " + item.code + " The description is: " + item.description + " The price is: " + item.price);
}
Output
The code is: code1 The description is: description1 The price is: price1
The code is: code2 The description is: description2 The price is: price2
The reason you're seeing that output is that you are not saving description along with the code inside the list, that is why the last description is saved within the description variable not all description values.
To solve this problem, you can create a simple Java Bean/POJO class
and wrap your data inside it and then you can simply fetch the value
you have saved it and then show it properly. Take a look at the code
below:
public class Launcher{
public static void main(String[] args) {
ArrayList<Item> items = new ArrayList<Item>();
File fn = new File("file.txt");
try {
Scanner readFile = new Scanner(fn);
while (readFile.hasNext()) {
String[] astring = readFile.nextLine().split(",");
String code = astring[0];
String description = astring[1];
String price = astring[2];
Item item = new Item(code, description, price);
items.add(item);
}
} catch (FileNotFoundException d) { // }
}
for (Item thing : items) {
System.out.println(String.format("The code is: %s\tThe description is: %s\tThe Price is %s",thing.getCode(),thing.getDescription(), thing.getPrice()));
}
}
}
class Item {
private String code;
private String description;
private String price;
public Item(String code, String description, String price) {
this.code = code;
this.description = description;
this.price = price;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public String getPrice() {
return price;
}
}

Java Hash map / Array List Count distinct values

I am pretty new into programming and I have an assignment to make, but I got stuck.
I have to implement a program which will read a CSV file (1 million+ lines) and count how many clients ordered "x" distinct products on a specific day.
The CSV looks like this:
Product Name | Product ID | Client ID | Date
Name 544 86 10/12/2017
Name 545 86 10/12/2017
Name 644 87 10/12/2017
Name 644 87 10/12/2017
Name 9857 801 10/12/2017
Name 3022 801 10/12/2017
Name 3021 801 10/12/2017
The result from my code is:
801: 2 - incorrect
86: 2 - correct
87: 2 - incorrect
Desired output is:
Client 1 (801): 3 distinct products
Client 2 (86): 2 distinct products
Client 3 (87): 1 distinct product
Additionally,
If I want to know how many clients ordered 2 distinct products I would like a result to look like this:
Total: 1 client ordered 2 distinct products
If I want to know the maximum number of distinct products ordered in a day, I would like the result to look like this:
The maximum number of distinct products ordered is: 3
I tried to use a Hash Map and Multimap by Google Guava (my best guess here), but I couldn't wrap my head around it.
My code looks like this:
package Test;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
public class Test {
public static void main(String[] args) {
//HashMultimap<String, String> myMultimap = HashMultimap.create();
Map<String, MutableInteger> map = new HashMap<String, MutableInteger>();
ArrayList<String> linesList = new ArrayList<>();
// Input of file which needs to be parsed
String csvFile = "file.csv";
BufferedReader csvReader;
// Data split by 'TAB' in CSV file
String csvSplitBy = "\t";
try {
// Read the CSV file into an ArrayList array for easy processing.
String line;
csvReader = new BufferedReader(new FileReader(csvFile));
while ((line = csvReader.readLine()) !=null) {
linesList.add(line);
}
csvReader.close();
} catch (IOException e) {
e.printStackTrace();
}
// Process each CSV file line which is now contained within
// the linesList list Array
for (int i = 0; i < linesList.size(); i++) {
String[] data = linesList.get(i).split(csvSplitBy);
String col2 = data[1];
String col3 = data[2];
String col4 = data[3];
// Determine if Column 4 has the desired date
// and count the values
if (col4.contains("10/12/2017")) {
String key = col3;
if (map.containsKey(key)) {
MutableInteger count = map.get(key);
count.set(count.get() + 1);
} else {
map.put(key, new MutableInteger(1));
}
}
}
for (final String k : map.keySet()) {
if (map.get(k).get() == 2) {
System.out.println(k + ": " + map.get(k).get());
}
}
}
}
Any advise or suggestion on how this can be implemented would be greatly appreciated.
Thank you in advance guys.
You could store a Setof productIds per clientId, and just take the size of that.
As a Set does not allow duplicate values, this will effectively give you the distinct number of productIds.
Also, I recommend that you give your variables meaningful name instead of col2, k, map... This will make your code more readable.
Map<String, Set<String>> distinctProductsPerClient = new HashMap<String, Set<String>>();
// Process each CSV file line which is now contained within
// the linesList list Array
// Start from 1 to skip the first line
for (int i = 1; i < linesList.size(); i++) {
String line = linesList.get(i);
String[] data = line.split(csvSplitBy);
String productId = data[1];
String clientId = data[2];
String date = data[3];
// Determine if Column 4 has the desired date
// and count the values
if (date.contains("10/12/2017")) {
if (!distinctProductsPerClient.containsKey(clientId)) {
distinctProductsPerClient.put(clientId, new HashSet<>());
}
distinctProductsPerClient.get(clientId).add(productId);
}
}
for (final String clientId : distinctProductsPerClient.keySet()) {
System.out.println(clientId + ": " + distinctProductsPerClient.get(clientId).size());
}
More advanced solution using Stream API (requires Java 9)
If you introduce the class OrderData(that represents a single line in the CSV) like this:
private static class OrderData {
private final String productName;
private final String productId;
private final String clientId;
private final String date;
public OrderData(String csvLine) {
String[] data = csvLine.split("\t");
this.productName = data[0];
this.productId = data[1];
this.clientId = data[2];
this.date = data[3];
}
public String getProductName() {
return productName;
}
public String getProductId() {
return productId;
}
public String getClientId() {
return clientId;
}
public String getDate() {
return date;
}
}
you can replace the for loop with this:
Map<String, Set<String>> distinctProductsPerClient2 = linesList.stream()
.skip(1)
.map(OrderData::new)
.collect(groupingBy(OrderData::getClientId, mapping(OrderData::getProductId, toSet())));
But I reckon this might be a little bit to complex if you're new into programming (although it might be a good exercise if you would try to understand what the above code does).

Adding HashSet/Array to HashMap error

I am a beginner at Java, and I'm having trouble understanding why I'm getting an error. I have a .csv file containing cities, provinces, and respective populations of Canada. I have been trying to read the file and then put the PROVINCE and POPULATION values into a HashMap (cana) via a key/value pair. I've created a HashSet (canada) to split up the .csv, and I would like to keep that as-is if possible.
My question is about the cana.add(provSet, pop1). I am getting an "cannot find symbol - method add(java.util.Set) error around the "put", and I can't figure out why. Can someone please help me understand what I've done wrong? Since I am a beginner, additional explanation would be greatly appreciated!
String filename = "canada.csv";
try
{
BufferedReader br = new BufferedReader(new FileReader("canada.csv"));
String line = null;
HashSet<String> canada = new HashSet<String>();
HashMap<Set<String>, Set<Integer>> cana = new HashMap<Set<String>, Set<Integer>>();
while((line=br.readLine())!=null) {
String city = line.split(",")[0];
canada.add(city);
String province = line.split(",")[1];
canada.add(province);
Set<String> provSet = new HashSet<String>(Arrays.asList(province));
String population = line.split(",")[2];
canada.add(population);
int p = new Integer(population);
Set<Integer> pop1 = new HashSet<Integer>(Arrays.asList(p));
cana.add(provSet, pop1); //ERROR
//Trying to find the most populated province
String maxProvince = "";
int maxProvPop = 0;
for(String province : cana.keySet()) {
int provPop = cana.get(province);
System.out.println(population);
if( provPop > maxProvPop )
{
maxProvPop = provPop;
maxProvince = province;
}
System.out.println("The most populated province is " + maxProvince + " with a population of " + maxProvPop);
}
I think you're mixing up the methods for HashSet and HashMap. You use the add method for HashSet, and put method for HashMap.
HashSet Documentation
HashMap Documentation

manipulate and sort text file

I am working on a project where I have been given a text file and I have to add up the points for each team and printout the top 5 teams.
The text file looks like this:
FRAMae Berenice MEITE 455.455<br>
CHNKexin ZHANG 454.584<br>
UKRNatalia POPOVA 453.443<br>
GERNathalie WEINZIERL 452.162<br>
RUSEvgeny PLYUSHCHENKO 191.399<br>
CANPatrick CHAN 189.718<br>
CHNHan YAN 185.527<br>
CHNCheng & Hao 271.018<br>
ITAStefania & Ondrej 270.317<br>
USAMarissa & Simon 264.256<br>
GERMaylin & Daniel 260.825<br>
FRAFlorent AMODIO 179.936<br>
GERPeter LIEBERS 179.615<br>
JPNYuzuru HANYU 197.9810<br>
USAJeremy ABBOTT 165.654<br>
UKRYakov GODOROZHA 160.513<br>
GBRMatthew PARR 157.402<br>
ITAPaul Bonifacio PARKINSON 153.941<br>
RUSTatiana & Maxim 283.7910<br>
CANMeagan & Eric 273.109<br>
FRAVanessa & Morgan 257.454<br>
JPNNarumi & Ryuichi 246.563<br>
JPNCathy & Chris 352.003<br>
UKRSiobhan & Dmitri 349.192<br>
CHNXintong &Xun 347.881<br>
RUSYulia LIPNITSKAYA 472.9010<br>
ITACarolina KOSTNER 470.849<br>
JPNMao ASADA 464.078<br>
UKRJulia & Yuri 246.342<br>
GBRStacey & David 244.701<br>
USAMeryl &Charlie 375.9810<br>
CANTessa & Scott 372.989<br>
RUSEkaterina & Dmitri 370.278<br>
FRANathalie & Fabian 369.157<br>
ITAAnna & Luca 364.926<br>
GERNelli & Alexander 358.045<br>
GBRPenny & Nicholas 352.934<br>
USAAshley WAGNER 463.107<br>
CANKaetlyn OSMOND 462.546<br>
GBRJenna MCCORKELL 450.091<br>
The first three letters represent the team.
the rest of the text is the the competitors name.
The last digit is the score the competitor recived.
Code so far:
import java.util.Arrays;
public class project2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] array = new String[41];
String[] info = new String[41];
String[] stats = new String[41];
String[] team = new String[41];
//.txt file location
FileInput fileIn = new FileInput();
fileIn.openFile("C:\\Users\\O\\Desktop\\turn in\\team.txt");
// txt file to array
int i = 0;
String line = fileIn.readLine();
array[i] = line;
i++;
while (line != null) {
line = fileIn.readLine();
array[i] = line;
i++;
}
//Splitting up Info/team/score into seprate arrays
for (int j = 0; j < 40; j++) {
team[j] = array[j].substring(0, 3).trim();
info[j] = array[j].substring(3, 30).trim();
stats[j] = array[j].substring(36).trim();
}
// Random stuff i have been trying
System.out.println(team[1]);
System.out.println(info[1]);
System.out.println(stats[1]);
MyObject ob = new MyObject();
ob.setText(info[0]);
ob.setNumber(7, 23);
ob.setNumber(3, 456);
System.out.println("Text is " + ob.getText() + " and number 3 is " + ob.getNumber(7));
}
}
I'm pretty much stuck at this point because I am not sure how to add each teams score together.
This looks like homework... First of all you need to examine how you are parsing the strings in the file.
You're saying: the first 3 characters are the country, which looks correct, but then you set the info to the 4th through the 30th characters, which isn't correct. You need to dynamically figure out where that ends and the score begins. There is a space between the "info" and the "stats," knowing that you could use String's indexOf function. (http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#indexOf(int))
Have a look at Maps.
A map is a collection that allows you to get data associated with a key in a very short time.
You can create a Map where the key is a country name, with value being the total points.
example:
Map<String,Integer> totalScore = new HashMap<>();
if (totalScore.containsKey("COUNTRYNAME"))
totalScore.put("COUNTRYNAME", totalScore.get("COUNTRYNAME") + playerScore)
else
totalScore.put("COUNTRYNAME",0)
This will add to the country score if the score exists, otherwise it will create a new totalScore for a country initialized to 0.
Not tested, but should give you some ideas:
public static void main(String... args)
throws Exception {
class Structure implements Comparable<Structure> {
private String team;
private String name;
private Double score;
public Structure(String team, String name, Double score) {
this.team = team;
this.name = name;
this.score = score;
}
public String getTeam() {
return team;
}
public String getName() {
return name;
}
public Double getScore() {
return score;
}
#Override
public int compareTo(Structure o) {
return this.score.compareTo(o.score);
}
}
File file = new File("path to your file");
List<String> lines = Files.readAllLines(Paths.get(file.toURI()), StandardCharsets.UTF_8);
Pattern p = Pattern.compile("(\\d+(?:\\.\\d+))");
List<Structure> structures = new ArrayList<Structure>();
for (String line : lines) {
Matcher m = p.matcher(line);
while (m.find()) {
String number = m.group(1);
String text = line.substring(0, line.indexOf(number) - 1);
double d = Double.parseDouble(number);
String team = text.substring(0, 3);
String name = text.substring(3, text.length());
structures.add(new Structure(team, name, d));
}
}
Collections.sort(structures);
List<Structure> topFive = structures.subList(0, 5);
for (Structure structure : topFive) {
System.out.println("Team: " + structure.getTeam());
System.out.println("Name: " + structure.getName());
System.out.println("Score: " + structure.getScore());
}
}
Just remove <br> from your file.
Loading file into memory
Your string splitting logic looks fine.
Create a class like PlayerData. Create one instance of that class for each row and set all the three fields into that using setters.
Keep adding the PlayerData objects into an array list.
Accumulating
Loop through the arraylist and accumulate the team scores into a hashmap. Create a Map to accumulate the team scores by mapping teamCode to totalScore.
Always store row data in a custom object for each row. String[] for each column is not a good way of holding data in general.
Take a look in File Utils. After that you can extract the content from last space character using String Utils e removing the <br> using it as a key for a TreeMap. Than you can have your itens ordered.
List<String> lines = FileUtils.readLines(yourFile);
Map<String, String> ordered = new TreeMap<>();
for (String s : lines) {
String[] split = s.split(" ");
String name = split[0].trim();
String rate = splt[1].trim().substring(0, key.length - 4);
ordered.put(rate, name);
}
Collection<String> rates = ordered.values(); //names ordered by rate
Of course that you need to adjust the snippet.

Categories

Resources