Add one number from an array to another (by column) - java

I have been trying to figure out how to add one number in an array to another number in an array. I parsed the numbers in a String into integers, and separated them by columns. Then, I added each column into an array.
What I would like to solve is how to add all the numbers in a column.
The numbers are from a text file.
// numbers.txt:
Bob, 100, 98, 95
Alex, 85, 90, 92
I already used bufferedReader and parsed the numbers, from String to int.
The challenge is adding the numbers by column.
For example, if there are 3 numbers in each array, I just want to add the first numbers in each array.
Q1 is [100, 98, 95]
Q2 is [85, 90, 92]
Only 100 + 85 together.
Below are the codes that I have so far.
Any help on how to proceed will be awesome! Thanks for your time.
int Q1 = Integer.parseInt(columns[1]);
int Q2 = Integer.parseInt(columns[2]);
ArrayList<Integer> Q1list = new ArrayList<>();
Q1list.add(Q1);
Q1list.add(Q2);
double total = 0.0;
for (int i = 0; i < Q1list.size(); i++) {
total += Q1list.get(i);
}
System.out.println(total);

Well, usually when you want to add up the numbers from the array in to a sum you want to iterate over all the indexes in that array. From the loop you've written I cannot see going to all the numbers into the array in any way.
Please revise how for loop is used!
Here is a good explanation, hope it helps
Java: Array with loop

I think you should have at least 2 columns array.
After don't forget your index (in your loop)
Code suiggested:
public static void main(String[] args) {
int [] q1 = { 100 , 98 , 95 };
int [] q2 = { 85 , 90 , 92 };
List<Integer> sumList = new ArrayList<>();
// First solution (what you ask)
sumList.add( q1[0] + q2[0] );
System.out.println("Add Q1[0] + Q2[0]: " + sumList.get(0));
// Second solution (add all)
for( int i = 0 ; i < q1.length ; i++)
{
sumList.add(q1[i] + q2[i]);
}
// Check your result
for( int i : sumList )
System.out.println("Result: " + i);
}
And result gives:
// First solution (what you ask)
Add Q1[0] + Q2[0]: 185
// Second solution (add all)
Result: 185
Result: 185
Result: 188
Result: 187
I find what you want:
// Scanner
StringTokenizer i1 = new StringTokenizer(" [100,98,95]", "[,]");
StringTokenizer i2 = new StringTokenizer(" [85,90,92]", "[,]");
List<Integer> q1List = new ArrayList<>();
List<Integer> q2List = new ArrayList<>();
while( i1.hasMoreTokens() ){
try {
Integer intRes = Integer.parseInt(i1.nextToken());
System.out.println("Test1: " + intRes);
q1List.add(intRes);
}
catch( NumberFormatException e) {}
}
while( i2.hasMoreTokens() ){
try {
Integer intRes = Integer.parseInt(i2.nextToken());
System.out.println("Test2: " + intRes);
q2List.add(intRes);
}
catch( NumberFormatException e) {}
}
// Second solution (add all)
for( int i = 0 ; i < q1List.size() ; i++)
{
sumList.add(q1List.get(i) + q2List.get(i));
}
// Check your result
for( int i : sumList )
System.out.println("Result 2 : " + i);
Sorry for the long time but I have to find on web the answer.
Simples reads file line by line and set new string for each new line...
After that, for each string you can use Strink tokenizer with delimiter in your case : ",".
Take carefull that your first parameter shall be:
null (code for that)
name (catch this string)
other (maybe try catch)
I find this link on stack:
Using Java 8, what is the most preferred and concise way of printing all the lines in a file?
Good luck

Related

How to Load a file in parallel arrays in java

I have a file that contains 30 lines of data in this format:
month-day-year-gas price
Here's some sample data:
May 02, 1994 1.04
Can someone tell me how to load this file in a parallel arrays of months, days, price in java? After this I will have to display the lowest price, and also the highest price, and the average price for each month.
For those who are wondering:
Parallel Arrays are arrays where the data in each array is directly related to any given record data row. The index of one array contains related data at the very same index in another array. For example:
Dimension[] dim = new Dimension[4];
dim[0] = new Dimension(1,11);
dim[1] = new Dimension(2,22);
dim[2] = new Dimension(3,33);
dim[3] = new Dimension(4,44);
int[] widths = {dim[0].width, dim[1].width, dim[2].width, dim[3].width};
int[] heights = {dim[0].height, dim[1].height, dim[2].height, dim[3].height};
for (int i = 0; i < widths.length; i++) {
int w = widths[i];
int h = heights[i];
System.out.println("Width: " + w + " | Height: " + h);
}
And the Console output would be:
Width: 1 | Height: 11
Width: 2 | Height: 22
Width: 3 | Height: 33
Width: 4 | Height: 44
The widths and heights Integer Arrays are considered Parallel Arrays since the data index in each array is directly Related To and Parallel to each other. It doesn't matter which parallel array you iterate through, the current iteration is related to all other parallel arrays.
You can quickly see why it's much better to utilize a Class with Member variables for this sort of thing instead of arrays especially when a multiple number of parallel arrays are involved however, there are some practical uses for it.
The task at hand:
As you read in your file you want to use the String#split() method to break that line into the desired chunks you want. The delimiter to use here for the split() method will be a white-space (" ") or the Regular Expression (RegEx) "\\s+", for example:
"This is my String".split("\\s+");
The RegEx argument within the split() method basically means, split the string on one or more white-spaces.
In the example below the Parallel Arrays are Class Member variables. The method that fills these Arrays with the pertinent data from file is named fillParallelArraysWithFileData() and it accepts one argument, which is the path and name of the data file. Here is the code:
private static String[] monthsArray;
private static int[] daysArray;
private static int[] yearsArray;
private static double[] pricesArray;
public static void fillParallelArrayWithFileData(final String filePath) {
Scanner read = null;
try {
read = new Scanner(new File(filePath));
/* First Read Pass
===============
Get the number of VALID data lines in file.
We need this count to know how large to size
our Parallel Arrays.
*/
int lineCount = 0; // counter
while (read.hasNextLine()) {
String line = read.nextLine().trim(); // Trim lead/trailing whitespaces (if any)
/* Skip past blank or comment lines. Lines that start with
a semicolon (;) or a hash character (#) are considered
comment lines here and are ignored. You can get rid of
those conditions if you like. */
if (line.equals("") || line.startsWith(";") || line.startsWith("#")) {
continue;
}
lineCount++; // Increment the counter.
}
/* Second Read Pass
================
Get the file data and fill Arrays...
Declare Arrays which will be our parallel arrays. */
monthsArray = new String[lineCount];
daysArray = new int[lineCount];
yearsArray = new int[lineCount];
pricesArray = new double[lineCount];
int indexIncrementer = 0;
// Start the read from beginning again...
read = new Scanner(new File(filePath));
while (read.hasNextLine()) {
String line = read.nextLine();
// Remove the comma in data line. Don't want it.
line = line.trim().replace(",", "");
// If the current line is blank or a comment then skip past it.
if (line.equals("") || line.startsWith(";") || line.startsWith("#")) {
continue;
}
// Split the current data line
String[] lineParts = line.split("\\s+");
monthsArray[indexIncrementer] = lineParts[0];
daysArray[indexIncrementer] = Integer.parseInt(lineParts[1]);
yearsArray[indexIncrementer] = Integer.parseInt(lineParts[2]);
pricesArray[indexIncrementer] = Double.parseDouble(lineParts[3]);
indexIncrementer++;
}
}
catch (FileNotFoundException ex) {
System.out.println("FILE NOT FOUND! [" + filePath + "]");
}
finally {
if (read != null) {
read.close();
}
}
}
And an example usage might be:
// Fill Arrays with File data.
fillParallelArrayWithFileData("GasPrices.txt");
// Get Gas price for month of July in 1994
String desiredMonth = "July";
int desiredYear = 1994;
// We could iterate through any one of the Parallel Arrays
for (int i = 0; i < pricesArray.length; i++) {
if (monthsArray[i].equalsIgnoreCase(desiredMonth) && yearsArray[i] == desiredYear) {
String m = "Date: " + monthsArray[i] + " ";
String d = daysArray[i] + ", ";
String y = yearsArray[i] + " - ";
String p = "Gas Price: $" + pricesArray[i];
System.out.println(m + d + y + p);
}
}
Output to console window would be something like:
Date: July 2, 1994 - Gas Price: $1.12

How to Add elements into ArrayList in non-continuous indexes

I'm trying to add elements into Arraylist using non-continuous indexes, for example:
I want to add an element at index 3 first before adding any element at indexes (0,1,2). I will also fill up the indexes at 0,1,2 later.
Here's my code:
public static void main(String[] args) throws FileNotFoundException {
List<Integer> numbers= new ArrayList<Integer>();
Scanner inp = new Scanner(System.in);
int[] x = {1,5,2,4,3,0};
System.out.println("Enter elements:"+"\n");
for(int i=0;i<x.length;i++) {
int index = x[i];
numbers.add(index, inp.nextInt());
}
I seem to get this error:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.ArrayList.rangeCheckForAdd(Unknown Source)
at java.util.ArrayList.add(Unknown Source)
at Test1.main(Test1.java:28)
I understand the error, but I don't seem to find a way out of this problem. Any help will be much appreciated. If at all there's another fancy data structure which allows me to do non-continuous indexing please let me know, excuse me if my questions doesn't make any sense.
Have you thought about using a Map?
Map<Integer,Integer> myNumbers = new HashMap<>();
myNumbers.put( 4, 100 );
myNumbers.put( 2, 199 );
myNumbers.put( 1, 150 );
System.out.println( myNumbers.get( 2 ) );
There are multiple implementations of Map, e.g. HashMap or TreeMap. Which one to go for, depends on your requirements, for example, if you want to store the elements in a certain order or if you don't care about the order.
In your use case the Map could be used like this:
int[] x = { 1,5,2,4,3,0 };
// Try this instead of the above array, as well:
//int[] x = { 1337, 42, 777 };
// The Map can take an (almost) unlimited number of entries and gaps between
// indices / keys (e.g. 0, 1, 7, 23) are no problem. Only elements that you
// *put* into the Map, are part of it.
Map<Integer,Integer> numbersMap = new TreeMap<>();
System.out.println( "Enter elements:\n" );
try( Scanner inp = new Scanner( System.in ) ) {
for( int i=0; i<x.length; i++ ) {
int index = x[i];
System.out.print( "Enter element " + index + ": " );
int userInput = inp.nextInt();
numbersMap.put( index, userInput );
}
}
System.out.println( "Your Map contains these entries:" );
for( Map.Entry<Integer,Integer> entry : numbersMap.entrySet() ) {
System.out.println( "Element[" + entry.getKey() + "] = " + entry.getValue() );
}
As your indices in the x array are continuos, without gaps, zero-based and known in advance, you could use something like this in this special case, as well:
int[] x = { 1,5,2,4,3,0 };
Integer[] output = new Integer[ x.length ];
System.out.println( "Enter elements:\n" );
try( Scanner inp = new Scanner( System.in ) ) {
for( int i=0; i<x.length; i++ ) {
int index = x[i];
System.out.print( "Enter element " + index + ": " );
int userInput = inp.nextInt();
output[ index ] = userInput;
}
}
List<Integer> numbers = Arrays.asList( output );
System.out.println( numbers );
But I'd prefer the Map approach as it's more flexible and less error-prone.
You have an arraylist, but you seem to forget that you haven't set the initial size of the arraylist. The add method that you use places the value at the given index and then shifts everything over to the right, but in this case, there's no index 1. In order to do so, you should either do
List<Integer> numbers= Arrays.asList(new Integer[10]),
which will make an arrayList with 10 "indexes" which hold a value of null, or
ArrayList<Integer> arr=new ArrayList<Integer>(Collections.nCopies(10, 0));,
which will create anarrayList with 10 "indexes" each of which hold a value of 0. For more information, look at this question which you indirectly ask.
Initial size for the ArrayList

How can I format a two row string so that items of the bottom row line up with items of the top row in java

So I have something set up like this:
public static void main(String[] args) {
String fun = ("3000.05 500 6000.987 70 8000.00");
String lame = ("3 5 6 7 8");
String total = (fun + "\n" + lame);
System.out.println(total);
}
How can I format the total string so that when it prints it prints like this:
3000.05 500 6000.987 70 8000.00
3 5 6 7 8
I've been looking forever and can't seem to find what i'm looking for. I'm just trying to right align each part of the bottom string to the farthest part of the top string.
you can use a for loop to go through each position on the string and create a new String to store what you are looking for. Ex.
public static void main(String[] args) {
String fun = ("3000.05 500 6000.987 70 8000.00");
String newStringToBePrinted="";
for(char c: fun){
if (!(c == ' '|| c == '.')){
String ch = c+"";
newStringToBePrinted+= ch;
} else{
newStringToBePrinted+="-";
}
}
String total = (fun + "\n" + newStringToBePrinted);
System.out.println(total);
}
hope this helps.
Here is how you can do this with (moderately) readable Stream code. The below method will work, but it's a bit ugly because AFAIK Java 8 does not yet have a clean 'zip' function for streams.
As well, because of the way String.format() works, I had to pad the first thing in the list separately, which is also kind of yucky, but I couldn't quickly think of a way to make it all fit.
private String formatStringPairs(String firstLine, String secondLine, String delimiter) {
String[] firstLineArray = firstLine.split(delimiter);
String[] secondLineArray = secondLine.split(delimiter);
String formattedStringPairs =
String.format("%" + firstLineArray[0].length() + "s", secondLineArray[0]) +
IntStream.range(1, firstLineArray.length)
.mapToObj(index -> String.format("%" + (firstLineArray[index].length() + 1) + "s", secondLineArray[index]))
.reduce("", String::concat);
return firstLine + "\n" + formattedStringPairs;
}
I look forward to seeing someone post a cleaner and better way!
This should work. I believe temp is the recreation of your variable lame but the code would be the same.
package funTest;
public class funT {
public static void main(String[] args) {
String fun = ("3000.05 500 6000.987 70 8000.00");
String s2 = new String(fun);
char first=' ';
String temp="";
for (int i = 0;i < s2.length(); i++){
System.out.println("Should be: 3,5,6,7,8");
//gets first
if (i!=0){
if (s2.charAt(i)==' '){
first=s2.charAt(i+1);
}
}else{
first=s2.charAt(i);
}
System.out.println("First = "+first);
/////
//add spaces
if (s2.charAt(i)!=' '){
temp+=' ';
}
if (i<s2.length()-1&&s2.charAt(i+1)==' '){
temp+=first;
} else if (i==s2.length()-1){
temp+=first;
}
}
temp = temp.substring(1);
System.out.println(s2+"\n"+temp);
}
}
Ok so tldr. Use the .format() method of PrintStream (for byte streams) or PrintWriter (for char streams)
Luckily System.out is one of these objects. Documentation for format can be found here
Here is a nice demonstration of using .format() I mentioned for your purpose, this code assumes the two strings have an equal number of numbers separated by whitespace. It also assumes that the second-row item length will be <= in length to its' first-row counterpart
public static void main(String[] args)
{
String fun = "3000.05 500 6000.987 70 8000.00";
String lame = "3 5 6 7 8";
String[] funs = fun.split(" ");
String[] lames = lame.split(" ");
for(String s : funs)
{
System.out.printf("%s\t", s);
}
System.out.printf("%n");
for(int i = 0; i < lames.length; i++) //use index iterator so we can get le funz
{
System.out.printf("%" + funs[i].length() + "s\t", lames[i]);
}
System.out.printf("%n");
}
Output:
run:
3000.05 500 6000.987 70 8000.00
3 5 6 7 8
BUILD SUCCESSFUL (total time: 0 seconds)
To improve this code more It might be possible to associate the first row and second row in a Map type object, and then when printing: set the padding size
to the size of the entry key or value which has the greatest length. Also add a handler for empty entries.

logically sorting a mixed string of uppercase letters and numbers

I've got a string of uppercase letters and numbers that I must 'logically' sort and store in a field in a database. I've got the update/change/inquire part into the database figured out. I'm struggle with logically sorting this string.
Here goes, I hope I can explain this well.
Given this set of strings
AB1
AB2
AB3
A11
AB10
I need these to alpha sort like so
A11
AB1
AB2
AB3
AB10
in order to achieve this, I believe I need to explode the string. because currently trying to alpha sort yields A11 AB1 AB10 AB2 AB3
EDIT: I need to be able to store an exploded string and a non exploded string to be able to sort with other programs.
Here is how I think they need to be broken up and stored in order to sort alpha
A11 - A 11
AB1 - AB 1
AB2 - AB 2
AB3 - AB 3
AB10 - AB 10
There are some constants. The string will be no larger than 5 positions. It will only contain upper case letters and numbers.
Here is as far as I've gotten with my code. writers block so i'm hoping for some help. I think I need to find if it starts with a letter, then find all the consecutive letters, move those left alight, then go to work on number, finding all the consecutive numbers and move those right aligned. Not sure how something like 'A1B1' would work either...
for(int ii = 0;ii < sectionString.length() && ii< SECTIONSPACES;ii++){
System.out.print(" Was previous a number? " + isPreviousANumber + "\n");
try{
String tmpString = sectionString.substring(ii,ii + 1 );
int positionInCharArray = Integer.parseInt(tmpString);
System.out.printf(" Position " + ii + " is number " + positionInCharArray + "\n");
isPreviousANumber = true;
}catch(Exception e){
System.out.printf(" Position " + ii + " number is not a number " + sectionString.substring(ii,ii) + "\n");
isPreviousANumber = false;
}
}
This remark "Not sure how something like 'A1B1' would work either..." somewhat increases the complexity of the problem. The following should work for all cases.
Method:
Divide the string into tokens. A token is either a letter or a consecutive run of digits. Pad each digits-token to five characters with leading spaces. Concatenate the tokens to make the exploded string.
From a 5 character original, the longest exploded string will be 17 characters.
The resulting exploded strings may be sorted by any program, or by a SQL "ORDERED BY" clause.
Examples:
1A1A1 " 1A 1A 1"
11A11 " 11A 11"
1111A " 1111A"
11111 "11111"
A1 "A 1"
A1B1 "A 1B 1"
A1C "A 1C"
A2 "A 2"
A2B1 "A 2B 1"
A10 "A 10"
A10B1 "A 10B 1"
A11 "A 11"
AA1 "AA 1"
AB1 "AB 1"
AB2 "AB 2"
AB10 "AB 10"
ABC "ABC"
Pseudocode:
// original = "section" string
exploded = ""
prevdigits = false
for ii from 1 to length(original) {
ch = original[ii]
if (ch is a digit) then {
if not prevdigits then {
token = ""
prevdigits = true
}
token = token+ch
} else { // letter
if prevdigits then {
exploded = exploded + spaces(5-length(token)) + token
prevdigits = false
}
exploded = exploded + ch
}
}
-Al.
Here is how I sort it using my radix sort idea:
public static String[] radixSort(String[] strings){
// Pad the strings
for(int i=0; i<strings.length; i++){
strings[i] = String.format("%-5s", strings[i]);
}
// Radix sort them
for (int digit = 0; digit < 5; digit++) {
final int i = digit;
Arrays.sort(strings, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.charAt(i) - o2.charAt(i);
}
});
}
// Then trim the whitespaces we used to pad
for (int i = 0; i < strings.length; i++) {
strings[i] = strings[i].trim();
}
return strings;
}
With input
String[] strings = new String[] { "AB1", "AB2", "AB3", "A11", "AB10" };
System.out.println(Arrays.toString(radixSort(strings)));
And output
[A11, AB1, AB2, AB3, AB10]
I am not sure this is the most efficient method but it gets the job done.
you could use another class as special representation for your strings. something like this:
public class AlphaNumericString implements Comparable<AlphaNumericString> {
public final String alphaPart;
public final Long numericPart;
public AlphaNumericString(String string) {
int index = 0;
while (index < string.length() && !Character.isDigit(string.charAt(index))) {
index++;
}
alphaPart = string.substring(0, index);
if (index < string.length()) {
numericPart = new Long(string.substring(index));
} else {
numericPart = null;
}
}
#Override
public int compareTo(AlphaNumericString other) {
int stringCompareResult = alphaPart != null ? alphaPart.compareTo(other.alphaPart) : -1;
if (stringCompareResult == 0) {
return numericPart != null ? numericPart.compareTo(other.numericPart) : -1;
} else {
return stringCompareResult;
}
}
#Override
public String toString() {
return (alphaPart != null ? alphaPart : "") + (numericPart != null ? numericPart : "");
}
}
You can turn your current strings into this class, sort and convert them back as needed
I would complete these strings with spaces to 5 symbols and after that would make Radix Sort . We can compare all symbols as chars.
String[] array = {"A11", "AB1", "AB2", "AB3", "AB10"};
int i, j, length;
for (i = 0; i < array.length; i++) {
length = array[i].length();
for (j = length; j < 5; j++) {
array[i] += " ";
}
}
Arrays.sort(array);
for (int k = 0; k<array.length; k++)
System.out.println(array[k]);
Here is my code. I'm sure it can be streamlined, it was one of those blackout moments where i had a brain child and needed to write. This would not work if the string of numbers was over 5 characters long...
updated:less ugly
private String buildPieceSortNumber(String pieceNumber){
final int INTSPACES = 5;
final String SPACE = " ";
String explodedSection = "";
char[] charArray = pieceNumber.toCharArray();
String ints = "";
for(int i = 0;i < charArray.length;i++){
if(Character.isDigit(charArray[i])){
//add to the int string
ints += charArray[i];
//check if the next character in the array is a number
int nextChar = i + 1;
//make sure we don't go past the end of the string
if(nextChar < charArray.length){
if(!Character.isDigit(charArray[nextChar])){
//end of numbers, take ints string, and add padding up to five positions
while(ints.length() < INTSPACES){
ints = SPACE + ints;
}
//add the int string to the end of the exploded string
explodedSection += ints;
//clear the int string
ints = "";
}
}else{
//end of numbers, take ints string, and add padding up to five positions
while(ints.length() < INTSPACES){
ints = SPACE + ints;
}
//add the int string to the end of the exploded string
explodedSection += ints;
//clear the int string
ints = "";
}
}else{
explodedSection += charArray[i];
}
}
return explodedSection;
Do you really need to sort the data before putting it in the database? Consider letting the database do the work for you.
Suppose you wrote the value straight into the database. Your database might allow you to do something like mine does. In DB2, to get only letters, I would translate all the digits to spaces, and then remove all the spaces. The same concept can apply to getting only digits.
SELECT replace(translate(inp, #spaces, #digits),' ','') as alpha,
int(replace(translate(inp, #spaces, #letters),' ','')) as nbr,
....
While this might be a normalized database approach, you might question performing this calculation every time data is retrieved from the table. So instead, do this when writing the the data into the table
INSERT INTO yourtable ( item, alpha, nbr, ..... )
VALUES (inp,
replace(translate(inp, #spaces, #digits),' ',''),
int(replace(translate(inp, #spaces, #letters),' ','')),
.....
)
To my view, this is simpler logic, less code, easier to test / debug, helping reduce risks of defects, and being easier for someone to maintain. Of course your mileage may vary depending on your database. But this approach seems worth consideration.

Is there an easy way to output a column-wise CSV?

I'm trying to output multiple lists of data, of varying length, to a CSV file. Each list should be a column in the output CSV file. Is there a straight-forward way of doing thing? If I were outputting each list as a row, I'd just loop over each list and output a return when I hit the end, but this approach does not work when working column-wise.
I thought of going over all the lists at once, item by item and incrementing a counter, but this would also fail because some lists are longer than others. To remedy this I would have to check at each iteration whether the counter is past the end of each list, which would be fairly expensive in terms of computations.
Thanks for any ideas!
I think this is pretty straight-forward:
public static void main(String... args) throws IOException {
ArrayList<ArrayList<String>> rows = getRandomData();
if (rows.size() == 0)
throw new RuntimeException("No rows");
// normalize data
int longest = 0;
for (List<String> row : rows)
if (row.size() > longest)
longest = row.size();
for (List<String> row : rows)
while (row.size() < longest)
row.add("");
if (longest == 0)
throw new RuntimeException("No colums");
// fix special characters
for (int i = 0; i < rows.size(); i++)
for (int j = 0; j < rows.get(i).size(); j++)
rows.get(i).set(j, fixSpecial(rows.get(i).get(j)));
// get the maximum size of one column
int[] maxColumn = new int[rows.get(0).size()];
for (int i = 0; i < rows.size(); i++)
for (int j = 0; j < rows.get(i).size(); j++)
if (maxColumn[j] < rows.get(i).get(j).length())
maxColumn[j] = rows.get(i).get(j).length();
// create the format string
String outFormat = "";
for (int max : maxColumn)
outFormat += "%-" + (max + 1) + "s, ";
outFormat = outFormat.substring(0, outFormat.length() - 2) + "\n";
// print the data
for (List<String> row : rows)
System.out.printf(outFormat, row.toArray());
}
private static String fixSpecial(String s) {
s = s.replaceAll("(\")", "$1$1");
if (s.contains("\n") || s.contains(",") || s.contains("\"") ||
s.trim().length() < s.length()) {
s = "\"" + s + "\"";
}
return s;
}
private static ArrayList<ArrayList<String>> getRandomData() {
ArrayList<ArrayList<String>> data = new ArrayList<ArrayList<String>>();
String[] rand = { "Do", "Re", "Song", "David", "Test", "4", "Hohjoh", "a \"h\" o", "tjo,ad" };
Random r = new Random(5);
for (int i = 0; i < 10; i++) {
ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < r.nextInt(10); j++)
row.add(rand[r.nextInt(rand.length)]);
data.add(row);
}
return data;
}
Output (pretty ugly since its random) (escapes):
Re , 4 , "tjo,ad" , "tjo,ad" ,
"tjo,ad" , "a ""h"" o" , , ,
Re , "a ""h"" o" , Hohjoh , "tjo,ad" , 4
4 , David , , ,
4 , Test , "tjo,ad" , Hohjoh , Re
Do , Hohjoh , Test , ,
Hohjoh , Song , , ,
4 , Song , , ,
4 , Do , Song , Do ,
Song , Test , Test , ,
It's worth having a look at http://commons.apache.org/sandbox/csv/
This also references some other CSV libraries.
Note that many answers have not considered strings which contain commas. That's the sort of reason why libraries are better than doing it yourself.
You can use String.format():
System.out.println(String.format("%4s,%4s,%4s", "a", "bb", "ccc"));
System.out.println(String.format("%4s,%4s,%4s", "aaa", "b", "c"));
The result will be a fixed column width of 4 characters - as long as the used values are shorter. Otherwise the layout will break.
a, bb, ccc
aaa, b, c
I'm not familiar with Java at all, but if you have a matrix oriented data type, you could fill the rows using easy looping, then transpose it, then write it out using easy looping. Your printing routine could handle null entries by outputting a null string, or fixed width spaces if you prefer.
Create an array of iterators (one for each list.) Then loop over the array, checking if the iterator hasNext(); if it does, output iterator.next(). Outputting commas and newlines is trivial. Stop when all iterators have returned hasNext()==false.
You can do something like this:
List<List<?>> listOfLists = new LinkedList<List<?>>();
List<Iterator<?>> listOfIterators = new LinkedList<Iterator<?>>();
for (List<?> aList : listOfLists) {
listOfIterators.add(aList.iterator());
}
boolean done = false;
while(!done)
{
done = true;
for (Iterator<?> iter : listOfIterators)
{
if (iter.hasNext())
{
Object obj = iter.next();
//PROCESS OBJ
done = false;
}
else
{
//PROCESS EMPTY ELEMENT
}
}
}
For CSV processing I have used this library several times: http://www.csvreader.com/java_csv.php Very simple and convenient.
Cheerz!
I would have to check at each iteration whether the counter is past the end of each list, which would be fairly expensive in terms of computations.
Get over it. This will, realistically, be small compared to the cost of actually doing the iteration, which in turn will be tiny compared to the cost of writing any given bit of text to the file. At least, assuming you have random access containers.
But you shouldn't be thinking in terms of a counter and indexing anyway; you should be thinking in terms of iterators (which sidestep the random-access question and simplify the code).
If you wanted to do this in one pair of loops and one method, you could do the following.
public static void writeCSV(PrintWriter pw, List<List<String>> columnsRows) {
for(int i=0;;i++) {
StringBuilder line = new StringBuilder();
boolean empty = true;
for (List<String> column : columnsRows) {
String text = i < column.size() ? column.get(i) : "";
found &= i >= column.size();
if (text.contains(",") || text.contains("\"") || text.contains("\n") || text.trim() != text)
text = '"' + text.replaceAll("\"", "\"\"") + '"';
line.append(text).append(',');
}
if (empty) break;
pw.println(line.substring(0, line.length()-1));
}
}
As an exercise, you could do this with one loop, but it wouldn't be as clear as to what its doing.
Using the sample data from #dacwe, this method takes 10 us (micro-seconds).

Categories

Resources