I have a .csv file that is formated like this:
ID,date,itemName
456,1-4-2020,Lemon
345,1-3-2020,Bacon
345,1-4-2020,Sausage
123,1-1-2020,Apple
123,1-2-2020,Pineapple
234,1-2-2020,Beer
345,1-4-2020,Cheese
I have already implemented the algorithm to go through the file, scan for the first number and sort it in a descending order and make a new output:
123,1-1-2020,Apple
123,1-2-2020,Pineapple
234,1-2-2020,Beer
345,1-3-2020,Bacon
345,1-4-2020,Cheese
345,1-4-2020,Sausage
456,1-4-2020,Lemon
My question is, how do I implement my algorithm to make an output that counts the duplicate first number entries and reformat it to make it look like this...
123,1-1-2020,1,Apple
123,1-2-2020,1,Pineapple
234,1-2-2020,1,Beer
345,1-3-2020,1,Bacon
345,1-4-2020,2,Cheese,Sausage
456,1-4-2020,1,Lemon
...so that it counts the number of occurrence for each ID, denote it with the number of times, and if the date of that ID is also the same, combine the item names to the same line. Below is my source code (each line in the .csv is made into an object named 'receipt' that has ID, date, and name with their respective get() methods):
public class ReadFile {
private static List<Receipt> readFile() {
List<Receipt> receipts = new ArrayList<>();
try {
BufferedReader reader = new BufferedReader(new FileReader("dataset.csv"));
// Move past the first title line
reader.readLine();
String line = reader.readLine();
// Start reading from second line till EOF, split each string at ","
while (line != null) {
String[] attributes = line.split(",");
Receipt attribute = getAttributes(attributes);
receipts.add(attribute);
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
return receipts;
}
private static Receipt getAttributes(String[] attributes) {
// Get ID located before the first ","
long memberNumber = Long.parseLong(attributes[0]);
// Get date located after the first ","
String date = attributes[1];
// Get name located after the second ","
String name = attributes[2];
return new Receipt(memberNumber, date, name);
}
// Parse the data into new file after sorting
private static void parse(List<Receipt> receipts) {
PrintWriter output = null;
try {
output = new PrintWriter("output.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// For each receipts, assert the text output stream is not null, print line.
for (Receipt p : receipts) {
assert output != null;
output.println(p.getMemberNumber() + "," + p.getDate() + "," + p.getName());
}
assert output != null;
output.close();
}
// Main method, accept input file, sort and parse
public static void main(String[] args) {
List<Receipt> receipts = readFile();
QuickSort q = new QuickSort();
q.quickSort(receipts);
parse(receipts);
}
}
The easiest way is to use a map.
Sample data from your file.
String[] lines = {
"123,1-1-2020,Apple",
"123,1-2-2020,Pineapple",
"234,1-2-2020,Beer",
"345,1-3-2020,Bacon",
"345,1-4-2020,Cheese",
"345,1-4-2020,Sausage",
"456,1-4-2020,Lemon"};
Create a map
as you read the lines, split them and add them to the map using the compute method. This will put the line in if the key (number and date) doesn't exist. Otherwise it simply appends the last item to the existing entry.
the file does not have to be sorted but the values will be added to the end as they are encountered.
Map<String, String> map = new LinkedHashMap<>();
for (String line : lines) {
String[] vals = line.split(",");
// if v is null, add the line
// if v exists, take the existing line and append the last value
map.compute(vals[0]+vals[1], (k,v)->v == null ? line : v +","+vals[2]);
}
for (String line : map.values()) {
String[] fields = line.split(",",3);
int count = fields[2].split(",").length;
System.out.printf("%s,%s,%s,%s%n", fields[0],fields[1],count,fields[2]);
}
For this sample run prints
123,1-1-2020,1,Apple
123,1-2-2020,1,Pineapple
234,1-2-2020,1,Beer
345,1-3-2020,1,Bacon
345,1-4-2020,2,Cheese,Sausage
456,1-4-2020,1,Lemon
can I get recommendation or like an advice for what should be used or be known to complete this task (in the most rudimentary way I guess). if someone would be willing to write a code that would be fantastic but vague answers on the neccesary knowledge or technique will suffice.
I would like a program where at the start you input characters either seperataed by pressing enter or a string that could be like chopped up into separate items of an array (I guess) - characters separated by a comma - and that would be then compared to a txt file that contains series of entries and only those that contain some of (meaning shorter) or all of the characters that have been provided at the start would be printed, perhaps even the print would be separated by a length of the entry (word).
Any ideas on how to do this? Also, can the results be printed somewhere else than the command line, like another txt file? Need to do this in java. Thanks.
Take a look at following example:
public class SimpleExample {
public static void main(String[] args) throws ClassNotFoundException {
Scanner inputNumbers = new Scanner(System.in);
List<Integer> listOfNumbersToStore = new ArrayList<>();
List<Integer> listOfNumbersToCheck = new ArrayList<>();
int number;
String answer;
boolean flag = true;
// do code within a loop while flag is true
do {
// print message to screen
System.out.print("Would you like to put new number to your file list (Y/N): ");
// get answer (Y/N) to continue
answer = inputNumbers.next();
// if answer is Y or y
if ("Y".equalsIgnoreCase(answer)) {
// print message
System.out.print("Put your number: ");
// get input integer and assign it to number
number = inputNumbers.nextInt();
// add that number to a list of numbers to store to file
listOfNumbersToStore.add(number);
} else if ("N".equalsIgnoreCase(answer)) {
flag = false;
}
} while (flag);
writeToFile(listOfNumbersToStore);
System.out.println("---------- Check numbers ----------");
flag = true; // set it again to true
//do code within a loop while flag is true
do {
System.out.print("Would you like to put new number to your check list (Y/N) : ");
answer = inputNumbers.next();
if ("Y".equalsIgnoreCase(answer)) {
System.out.print("Put your number: ");
number = inputNumbers.nextInt();
listOfNumbersToCheck.add(number);
} else if ("N".equalsIgnoreCase(answer)) {
flag = false;
}
} while (flag);
// get a list from a file
List<Integer> readFromFile = readFromFile();
// check if there are any common elements within compared lists
boolean areThereAnyCommonElements = !Collections.disjoint(
listOfNumbersToCheck, readFromFile);
//create a new treeset used for containing unique elements and ordering it naturally, from 0 to n
Set<Integer> set = new TreeSet<>(listOfNumbersToCheck);
set.retainAll(readFromFile);
// print these messages
System.out.println("Are there any common integers between a list from a file and checking list? " + areThereAnyCommonElements);
System.out.println("Those integers are: " + set.toString());
}
/**
* List implements Seriazable interface, therefore store it to a file
* serialized
*
* #param numberOfIntegers
*/
public static void writeToFile(List<Integer> numberOfIntegers) {
try {
// create a file output stream to write to the file with the specified name.
FileOutputStream fileOutputStream = new FileOutputStream("tekstDataOutputStream");
// writes the serialization stream header to the underlying file stream;
ObjectOutputStream dataOutputStream = new ObjectOutputStream(new BufferedOutputStream(fileOutputStream));
// write a list to object output stream
dataOutputStream.writeObject(numberOfIntegers);
//close them
dataOutputStream.close();
fileOutputStream.close();
} catch (IOException ioE) {
System.err.println("JVM reported an error! Take a look: " + ioE);
}
}
public static List<Integer> readFromFile() throws ClassNotFoundException {
//create an empty list of integers
List<Integer> result = new ArrayList<>();
try {
//opening a connection to an actual file
FileInputStream fis = new FileInputStream("tekstDataOutputStream");
//used for reading from a specified input stream
ObjectInputStream reader = new ObjectInputStream(fis);
//get that list
result = (List<Integer>) reader.readObject();
//close streams
reader.close();
fis.close();
} catch (IOException ioE) {
System.err.println("JVM reported an error! Take a look: " + ioE);
}
return result;
}
}
I have class Account which have username, fullName, password, id and points.
All accounts are saved in a file.I have many accounts in my file, not just one. This is example of one account in text file.
Miljan9602 Rakita Miljan miljan123 1463433398614 0.0
username, full name, password, id and points
Now, for example if i want to change points for my username. First thing i would do is go through all lines in file and compare all usernames, if i find equal username. I would change point's. This is my idea how to do it. Just dont know how to edit it in file.
public void edit(String username, double points)
{
File f = new File("Accounts.txt");
// file doesnt exist, return from method
if(!f.exists())
return;
Scanner sc = null;
try
{
sc = new Scanner(f);
while(sc.hasNextLine())
{
String line = sc.nextLine(); // Take whole line
String split[] = line.split(" "); // Split it so i can check username
if(split[0].equals(username))
{
String change = Double.toString(points); // Make string from double
split[5] = change; // on fifth index are points
/* My question is now how to edit file and to replace my new points
* with old points ?
* Miljan9602 Rakita Miljan miljan123 1463433398614 0.0 <- Need to change this 0.0 with split[4];
*/
}
}
}catch(Exception e)
{
e.printStackTrace();
}finally{
// finally will always close file
sc.close();
}
You could use the Apache's Commons IO library. Everything you'll need, and more, can be found there. Also, here is the GitHub mirror of Commons IO. Worth a look through.
{
File f = new File("Accounts.txt");
FileWriter fw = new FileWriter(f);
// file doesnt exist, return from method
if(!f.exists())
return;
Scanner sc = null;
try
{
sc = new Scanner(f);
while(sc.hasNextLine())
{
String line = sc.nextLine(); // Take whole line
String split[] = line.split(" "); // Split it so i can check username
if(split[0].equals(username))
{
String change = Double.toString(points); // Make string from double
split[5] = change; // on fifth index are points
/* My question is now how to edit file and to replace my new points
* with old points ?
* Miljan9602 Rakita Miljan miljan123 1463433398614 0.0 <- Need to change this 0.0 with split[4];
*/
for(int i = 0; i < spit.length(); i++{
fw.write(split[i] + " ");
}
System.getProperty("line.separator");
}
}
}catch(Exception e)
{
e.printStackTrace();
}finally{
// finally will always close file
sc.close();
fw.close();
}
This should work
As one has to write the entire read text back to the file system, use Files.readAllLines().
Path path = Paths.get(".../Accounts.txt");
Charset charset = StandardCharsets.UTF_8;
List<String> lines = new ArrayList<>();
if (Files.exists()) {
Files.readAllLines(path, charset);
for (int i = 0; i < lines.size(); ++i) {
String split[] = lines.get(i).split(" ");
if (split[0].equals(username)) {
String change = String.valueOf(points);
split[5] = change; // on fifth index are points
StringBuilder sb = new StringBuilder();
for (String value : split) {
if (sb.length() != 0) {
sb.append(' ');
}
sb.append(value);
}
lines.set(i, sb.toString()); // Changes the line.
Files.write(path, lines, charset);
break; // leave loop
}
}
}
More explained
To alter a single line of a text file, one in principle has to load the entire text and after altering the line, safe it entirely.
The reason is that the file can shrink or grow, depending on the line changes.
Even with some twists this is not optimal.
Files.readAllLines is a nice method for that. One might also change the format:
Fixed length records (lines) allow a RandomAccessFile. However a text risks being manually edited so the file gets corrupted, and one also has limited field lengths.
The .properties format allows access with the Properties class. Requirement is a key, and a format key = value. Also the text has some escaping (\).
One could keep Accounts.txt in core, say in a class Accounts, representing all as a Map from user name to Account.
class Account {
public final String userName; // Unmodifiable key
public String password;
...
}
class Accounts {
private Map<String, Account> accountsByUserName = new HashMap<>();
public void loadAccounts() throws IOException { ... }
public void saveAccounts() throws IOException { ... }
public Optional<Account> getAccountByUserName(String userName) { ... }
public void deleteAccountByUserName(String userName) { ... }
public void createAccount(Account account) throws AlreadyExistsException { ... }
}
In my journey to complete this program I've run into a little hitch with one of my methods. The method I am writing reads a certain .txt file and creates a HashMap and sets every word found as a Key and the amount of time it appears is its Value. I have managed to figure this out for another method, but this time, the .txt file the method is reading is in a weird format. Specifically:
more 2
morning's 1
most 3
mostly 1
mythology. 1
native 1
nearly 2
northern 1
occupying 1
of 29
off 1
And so on.
Right now, the method is returning only one line in the file.
Here is my code for the method:
public static HashMap<String,Integer> readVocabulary(String fileName) {
// Declare the HashMap to be returned
HashMap<String, Integer> wordCount = new HashMap();
String toRead = fileName;
try {
FileReader reader = new FileReader(toRead);
BufferedReader br = new BufferedReader(reader);
// The BufferedReader reads the lines
String line = br.readLine();
// Split the line into a String array to loop through
String[] words = line.split(" ");
// for loop goes through every word
for (int i = 0; i < words.length; i++) {
// Case if the HashMap already contains the key.
// If so, just increments the value.
if (wordCount.containsKey(words[i])) {
int n = wordCount.get(words[i]);
wordCount.put(words[i], ++n);
}
// Otherwise, puts the word into the HashMap
else {
wordCount.put(words[i], 1);
}
}
br.close();
}
// Catching the file not found error
// and any other errors
catch (FileNotFoundException fnfe) {
System.err.println("File not found.");
}
catch (Exception e) {
System.err.print(e);
}
return wordCount;
}
The issue is that I'm not sure how to get the method to ignore the 2's and 1's and 29's of the .txt file. I attempted making an 'else if' statement to catch all of these cases but there are too many. Is there a way for me to catch all the ints from say, 1-100, and exlude them from being Keys in the HashMap? I've searched online but have turned up something.
Thank you for any help you can give!
How about just doing wordCount.put(words[0],1) into wordcount for every line, after you've done the split. If the pattern is always "word number", you only need the first item from the split array.
Update after some back and forth
public static HashMap<String,Integer> readVocabulary(String toRead)
{
// Declare the HashMap to be returned
HashMap<String, Integer> wordCount = new HashMap<String, Integer>();
String line = null;
String[] words = null;
int lineNumber = 0;
FileReader reader = null;
BufferedReader br = null;
try {
reader = new FileReader(toRead);
br = new BufferedReader(reader);
// Split the line into a String array to loop through
while ((line = br.readLine()) != null) {
lineNumber++;
words = line.split(" ");
if (words.length == 2) {
if (wordCount.containsKey(words[0]))
{
int n = wordCount.get(words[0]);
wordCount.put(words[0], ++n);
}
// Otherwise, puts the word into the HashMap
else
{
boolean word2IsInteger = true;
try
{
Integer.parseInt(words[1]);
}
catch(NumberFormatException nfe)
{
word2IsInteger = false;
}
if (word2IsInteger) {
wordCount.put(words[0], Integer.parseInt(words[1]));
}
}
}
}
br.close();
br = null;
reader.close();
reader = null;
}
// Catching the file not found error
// and any other errors
catch (FileNotFoundException fnfe) {
System.err.println("File not found.");
}
catch (Exception e) {
System.err.print(e);
}
return wordCount;
}
To check if a String contains a only digits use StringĀ“s matches() method, e.g.
if (!words[i].matches("^\\d+$")){
// NOT a String containing only digits
}
This wont require checking exceptions and it doesnt matter if the number wouldnt fit inside an Integer.
Option 1: Ignore numbers separated by whitespace
Use Integer.parseInt() or Double.parseInt() and catch the exception.
// for loop goes through every word
for (int i = 0; i < words.length; i++) {
try {
int wordAsInt = Integer.parseInt(words[i]);
} catch(NumberFormatException e) {
// Case if the HashMap already contains the key.
// If so, just increments the value.
if (wordCount.containsKey(words[i])) {
int n = wordCount.get(words[i]);
wordCount.put(words[i], ++n);
}
// Otherwise, puts the word into the HashMap
else {
wordCount.put(words[i], 1);
}
}
}
There is a Double.parseDouble(String) method, which you could use in place of Integer.parseInt(String) above if you wanted to eliminate all numbers, not just integers.
Option 2: Ignore numbers everywhere
Another option is to parse your input one character at a time and ignore any character that isn't a letter. When you scan whitespace, then you could add the word generated by the characters just scanned in to your HashMap. Unlike the methods mentioned above, scanning by character would allow you to ignore numbers even if they appear immediately next to other characters.
So here is the method which is reading from the file, it then splits the information by the # sign. which is where a new month begins in the text file
public static String readPurchaseOrder(Scanner sc) {
final String DELIMITER = "#";
try {
while (sc.hasNext()) {
sc.useDelimiter(DELIMITER);
String data = sc.next();
return data;
}
} catch (Exception e) {
System.out.println(e);
}
sc.close();
return null;
}
The text file contains information shown below up to the 12th month
04/01/12#PNW-1234#PA/1234#10
15/01/12#BSE-5566#bT/4674#5#
08/02/12#PNE-3456#Xk/8536#1#
07/03/12#PEA-4567#ZR/7413#3
09/03/12#ESE-6329#HY/7195#30#
03/04/12#ESE-5577#LR/4992#12
23/04/12#PNW-1235#HY/7195#2#
09/05/12#ESE-6329#PV/5732#6
25/05/12#BSE-5566#PV/5732#10#
08/06/12#PNE-3457#kD/9767#1
31/06/12#EMI-6329#ZR/7413#10#
03/07/12#EMI-6329#PV/5732#12
25/07/12#BSE-5566#bT/4674#5#
I am using this to output the information from the file split by the #
for (int i = 0; i <12; i ++){
String str[] = InputFileData.readPurchaseOrder(sC).split("\\n");
for(String s : str){
System.out.println(s);
}
It outputs the data like this
04/01/12#PNW-1234#PA/1234#10
15/01/12#BSE-5566#bT/4674#5
08/02/12#PNE-3456#Xk/8536#1
07/03/12#PEA-4567#ZR/7413#3
09/03/12#ESE-6329#HY/7195#30
03/04/12#ESE-5577#LR/4992#12
23/04/12#PNW-1235#HY/7195#2
09/05/12#ESE-6329#PV/5732#6
25/05/12#BSE-5566#PV/5732#10
I want to store each individual line in an array, so I can then further split up the line to its each respective variables
If you would like to collect the results in an array, one line per array element, the easiest way to do it is to use a list (since you don't know in advance the number of lines), and then convert it to an array. The size of an array has to be declared in advance, so you want to use a more flexible data structure if you don't know how big it's going to be.
public static String[] readPurchaseOrder(Scanner sc) {
final String DELIMITER = "#";
List<String> results = new ArrayList<>();
try {
while (sc.hasNext()) {
sc.useDelimiter(DELIMITER);
String data = sc.next();
results.add(data); // add the line to the list
}
} catch (Exception e) {
System.out.println(e);
}
sc.close();
// convert the list to an array and return it.
return results.toArray(new String[results.size()]);
}