How to loop arraylist string with increment in Apache POI - java

I am stuck with my loop where I want to loop the arraylist of string that contain data like this
[A,B,C,D,E,F]
where I want to loop the arraylist at the top row only like the image down below. The result that I get when I run the code here is the the same as the first image.
Row roww1 = sheet.createRow(0);
for (int i = 0; i < shiftname.size(); i++) {
Cell toprow = roww1.createCell(i + 4);
toprow.setCellValue(shiftname.get(i));
}
This is the outcome that I want it to be

Related

Why ArrayIndexOutOfBoundsException error on Jtable having one element

I want to add jTable elements to a list. It works fine when jTable have more then one element, but it gives me the the following error when jTable have one element. Why so? How to resolve it. Thank You.
Here is the error.
Error: Exception in thread "AWT-EventQueue-0"
java.lang.ArrayIndexOutOfBoundsException: 1 >= 1
Here is the code:
DefaultTableModel table_tags = (DefaultTableModel)this.jTable_selectedTags.getModel();
int rowCount=table_tags.getRowCount();
Vector data = table_tags.getDataVector();
Vector row = (Vector) data.elementAt(1);
int mColIndex = 0;
List tags_data = new ArrayList(rowCount);
for (int i = 0; i < table_tags.getRowCount(); i++) {
row = (Vector) data.elementAt(i);
tags_data.add(row.get(mColIndex));
}
System.out.println(tags_data);
The issues was in accessing of index. So, after changing
Vector row = (Vector) data.elementAt(1);
to
Vector row = (Vector) data.elementAt(0);
It worked fine.
Your for loop is wrong. You iterate the table using this condition:
for (int i = 0; i < table_tags.getRowCount(); i++)
Problem is that you use the row count to perform the iteration. Given only one element in the Vector your row count would indeed by 1 but using this to iterate over the table would cause it to fail, since the only element at the table would at index 0. Change your above condition to:
for (int i = 0; i < table_tags.getRowCount() - 1; i++)
And I suppose you'll be fine.

Export Java HashMap to xlsx

I need convert HashMaps to xlsx using poi. For sheet data2 i need something like that:
table1:
But i have table2:
Here's my list of HashMaps:
rows=[{kol2=s, kol1=s}, {kol2=bbbb, kol3=bbbb, kol1=aaaa}, {kol2=bbbb, kol3=bbbb, kol1=aaaa}, {kol2=bbbb, kol3=bbbb, kol1=aaaa}, {kol2=s, kol1=s}]}
Here's my code:
XSSFWorkbook workBook = new XSSFWorkbook();
XSSFSheet sheet = workBook.createSheet("data");
XSSFSheet sheet2 = workBook.createSheet("data2");
int rowCount = 0;
int help = 1;
List<HashMap<String, Object>> rows = ((List<HashMap<String, Object>>) x);
int rowCount2 = 0;
int header = 1;
Row header2 = sheet2.createRow(0);
for (int i = 0; i < rows.size(); i++) {
int li = 0;
Row row2 = sheet2.createRow(++rowCount2);
HashMap<String, Object> row = rows.get(i);
int columnCount2 = 0;
for (HashMap.Entry<String, Object> subElement : row.entrySet()) {
if (subElement.getValue() != null) {
if (i == li) {
Cell cell = header2.createCell(header);
cell.setCellValue(subElement.getKey().toString());
header++;
}
li++;
Cell cell2 = row2.createCell(++columnCount2);
cell2.setCellValue(subElement.getValue().toString());
}
}
}
Someone can help?
Iterating over a HashMap's EntrySet
The first problem is that you are iterating over the entrySet of your HashMap
for (HashMap.Entry<String, Object> subElement : row.entrySet()) {
// no guaranteed order
}
Looking at the JavaDoc of the Set#iterator() method you will see this:
Returns an iterator over the elements in this set. The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee).
There are Sets which are ordered (such as the TreeSet), but since you are using a HashMap, your EntrySet won't be ordered too.
Notice the column order in your sheet is kol2-kol3-kol1. Don't you want it to be kol1-kol2-kol3?
Not creating empty columns
You are forgetting to create empty cells for columns you don't have in your Map.
if (subElement.getValue() != null) {
// there won't be an empty cell if you e.g. don't have kol2 in your rows Map,
// since this just skips your current value
}
This is why you end up with something like:
kol2 kol3 kol1
s s
bbbb bbbb aaaa
...
instead of:
kol2 kol3 kol1
s s
bbbb bbbb aaaa
...
Creating the header row inside the loop
By creating the header row inside your loop, you are making your solution more complicated than necessary. It would be much easier just to create the header row and then loop over your entries in the List.
if (i == li) {
Cell cell = header2.createCell(header);
cell.setCellValue(subElement.getKey().toString());
header++;
}
If you are doing this outside the loop, there is no need for the li and the header variable
Suggested solution
I would (for a start) come up with something like this (I added some extra comments I normally wouldn't put there to make more clear what the intentions are and what aspects of the solution you need to understand):
XSSFSheet sheet2 = workBook.createSheet("data2");
List<HashMap<String, Object>> rows = ((List<HashMap<String, Object>>) x);
List<String> headers = Arrays.asList("kol1", "kol2", "kol3");
int currentRowNumber = 0;
// create header row
Row header = sheet2.createRow(currentRowNumber);
for (int i = 0; i < headers.size(); i++) {
Cell headerCell = header.createCell(i);
headerCell.setCellValue(headers.get(i));
}
// create data rows (we loop over the rows List)
for (int i = 0; i < rows.size(); i++) {
HashMap<String, Object> row = rows.get(i);
// we neet to increment the rowNumber for the row in the sheet at the beginning of
// each row. entry 0 in the rows List is in sheetRow 1, entry 1 in sheetRow 2, etc.
currentRowNumber++;
Row sheetRow = sheet2.createRow(currentRowNumber);
// we can now loop over the columns inside the row loop (using the headers List)
// we create a Cell for each column, but only fill it if there is
for (int j = 0; j < headers.size(); j++) {
Cell cell = sheetRow.createCell(j);
// only fill the cell if we are having data in the row map for the current column
String currentColumnName = headers.get(j);
if (row.containsKey(currentColumnName)) {
cell.setCellValue(row.get(currentColumnName).toString());
}
}
}
If you want a different column order, just change the header List and you are done (e.g. Arrays.asList("kol2", "kol3", "kol1")).

How to add numbers inside 2D arrays

So I want to add the rows and columns in this 2D array right here.
The array looks like this:
int[][] array = new int[3][3];
array[0][0] = 0;
array[1][0] = 0;
array[2][0] = 0;
array[1][0] = 0;
array[1][1] = 1;
array[1][2] = 2;
array[2][0] = 0;
array[2][1] = 2;
array[2][2] = 4;
So I have this set up in a 3x3 matrix on a sheet of paper and the first row should print out 0, second row should print out 3, third row should print out 6
first column should print out 0, second column should print out 3, third column should print out 6.
I have no clue where to start and I just need to see how to do this problem, because I have a few questions after this that involve this, so if you can just give me code that I can read it would be really helpful! Thanks!
The simplest way is to create two for loops - outer one goes through the rows, inner one goes through the columns. Then sum up the values for every column of a given row.
int[] colSums = {0,0,0};
for (int c=0;c<array.length;c++) {
int rowSum = 0;
for (int c2=0;c2<array[c].length;c2++) {
rowSum += array[c][c2];
colSums[c2] += array[c][c2];
}
System.out.println("Sum of row "+c+": "+rowSum);
}
for (int c=0;c<colSums.length;c++) {
System.out.println("Sum of column "+c+": "+colSums[c]);
}

How to speed up autosizing columns in apache POI?

I use the following code in order to autosize columns in my spreadsheet:
for (int i = 0; i < columns.size(); i++) {
sheet.autoSizeColumn(i, true);
sheet.setColumnWidth(i, sheet.getColumnWidth(i) + 600);
}
The problem is it takes more than 10 minutes to autosize each column in case of large spreadsheets with more than 3000 rows. It goes very fast for small documents though. Is there anything which could help autosizing to work faster?
Solution which worked for me:
It was possible to avoid merged regions, so I could iterate through the other cells and finally autosize to the largest cell like this:
int width = ((int)(maxNumCharacters * 1.14388)) * 256;
sheet.setColumnWidth(i, width);
where 1.14388 is a max character width of the "Serif" font and 256 font units.
Performance of autosizing improved from 10 minutes to 6 seconds.
The autoSizeColumn function itself works not perfect and some columns width not exactly fit the data inside. So, I found some solution that works for me.
To avoid crazy calculations let give that to autoSizeColumn() function:
sheet.autoSizeColumn(<columnIndex>);
Now, our column autosized by library but we wont to add a little bit more to the current column width to make table looks fine:
// get autosized column width
int currentColumnWidth = sheet.getColumnWidth(<columnIndex>);
// add custom value to the current width and apply it to column
sheet.setColumnWidth(<columnIndex>, (currentColumnWidth + 2500));
The full function could looks like:
public void autoSizeColumns(Workbook workbook) {
int numberOfSheets = workbook.getNumberOfSheets();
for (int i = 0; i < numberOfSheets; i++) {
Sheet sheet = workbook.getSheetAt(i);
if (sheet.getPhysicalNumberOfRows() > 0) {
Row row = sheet.getRow(sheet.getFirstRowNum());
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
int columnIndex = cell.getColumnIndex();
sheet.autoSizeColumn(columnIndex);
int currentColumnWidth = sheet.getColumnWidth(columnIndex);
sheet.setColumnWidth(columnIndex, (currentColumnWidth + 2500));
}
}
}
}
P.S. Thanks Ondrej Kvasnovsky for the function https://stackoverflow.com/a/35324693/13087091
The autosizeColumn() function very slow and unneficient. Even authors of apache POI mentioned in docs, that:
This process can be relatively slow on large sheets, ...
Calculating and setting the cell's width manually is way faster - in my case I reduced the time from ~25,000ms to ~1-5ms.
This is how to achieve it (I was basing on Vladimir Shcherbukhin's answer:
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
final int[] maxNumCharactersInColumns = new int[headers.length]; // maximum number of characters in columns. Necessary to calculate the cell width in most efficient way. sheet.autoSizeColumn(...) is very slow.
Row headersRow = sheet.createRow(0);
CellStyle headerStyle = createHeadersStyle(workbook); // createHeadersStyle() is my own function. Create headers style if you want
for (int i = 0; i < headers.length; i++) { // create headers
Cell headerCell = headersRow.createCell(i, CELL_TYPE_STRING);
headerCell.setCellValue(headers[i]);
headerCell.setCellStyle(headerStyle);
int length = headers[i].length();
if (maxNumCharactersInColumns[i] < length) { // adjust the columns width
maxNumCharactersInColumns[i] = length + 2; // you can add +2 if you have filtering enabled on your headers
}
}
int rowIndex = 1;
for (List<Object> rowValues : rows) {
Row row = sheet.createRow(rowIndex);
int columnIndex = 0;
for (Object value : rowValues) {
Cell cell = createRowCell(row, value, columnIndex); // createRowCell() is my own function.
int length;
if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
String cellValue = cell.getStringCellValue();
// this is quite important part. In some excel spreadsheet you can have a values with line-breaks. It'll be cool to handle that scenario :)
String[] arr = cellValue.split("\n"); // if cell contains complex value with line breaks, calculate only the longest line
length = Arrays.stream(arr).map(String::length).max(Integer::compareTo).get();
} else {
length = value != null ? value.toString().length() : 0;
}
if (maxNumCharactersInColumns[columnIndex] < length) { // if the current cell value is the longest one, save it to an array
maxNumCharactersInColumns[columnIndex] = length;
}
columnIndex++;
}
rowIndex++;
}
for (int i = 0; i < headers.length; i++) {
int width = (int) (maxNumCharactersInColumns[i] * 1.45f) * 256; // 1.45f <- you can change this value
sheet.setColumnWidth(i, Math.min(width, MAX_CELL_WIDTH)); // <- set calculated cell width
}
sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, headers.length - 1));
ByteArrayOutputStream output = new ByteArrayOutputStream();
workbook.write(output);
workbook.close();
Unfortunately I don't have enough reputations yet to add comments in answers. So here some annotations:
When using Row row = sheet.getRow(sheet.getFirstRowNum()); be shure, this row contains at least a value in the last column. Otherwise the cellIterator will end too early, i.e. if a subsequent row has a value in this column, this column will not be autosized. This problem is bypassed if rowcontains the headers (names of the columns). Or explicit use a known header row, e.g.
int indexOfHeaderRow = ...;
...
Row row = sheet.getRow(indexOfHeaderRow);
Jakub SÅ‚owikowski
sheet.setColumnWidth(i, Math.min(width, MAX_CELL_WIDTH)); // <- set calculated cellwidth
I'm not shure about this line because there is no information about content of MAX_CELL_WIDTH - perhaps overall maximum? So I used instead:
sheet.setColumnWidth(i, Math.max(width, 2048));
2048 seams to be the default width? This value prevents extremely narrow widths for empty columns.

Dynamically creating rows in Excel sheet with Apache POI

I'm writing a program to read a large xml file and create an excel file from it. The attributes of each node will be the column headers in excel file. I created a Dom object and got the nodelist. I need to iterate through it and for each node, i need to add a row in excel sheet with the node's attributes values as column values. So, when iterating, i need to create rows dynamically. How can i do it? I dont see a functionality to add created rows in apache POI, so far what i have seen is to define new rows everytime. I'm unable to do it since it has more than 5000 entries. Basically what i want to do is:
Node node = null;
HSSFRow datarow = null;
for (int i = 0; i < nodeList.getLength(); i++) {
node = nodeList.item(i);
datarow = spreadSheet.createRow(i);
//set values for data row here, and add it.
//so in the loop, next time the same variable will be assigned to spreadSheet.createRow(1) etc.
}
I understand that the createRow is invoked from spreadSheet, which will add the row to it. But in the loop, the same variable will be assigned to other rows too, so i think finally i will get only 1 row. Please advice me on this.
Try the following
Node node = null;
HSSFRow datarow = null;
for (int i = 0; i < nodeList.getLength(); i++) {
// On each loop you get the value of node item
node = nodeList.item(i);
//For every new node list you will create a row
datarow = spreadSheet.createRow(i);
//Finally set the node value to the columns of the newly created Row
}
Hope this helps !!
createRow has already created the row in the worksheet and is returning a reference to the newly created row. You will lose this reference on the next loop iteration but it will not remove/override the previous row from the worksheet. You can expect to have the correct number of rows in the end.
int totalRows = 5;
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Data");
HSSFRow datarow = null;
for (int i = 0; i <= totalRows; i++) {
datarow = sheet.createRow(i);
datarow.createCell(0).setCellValue(Integer.toString(i));
}
System.out.println("Total Rows: " + sheet.getLastRowNum());
System.out.println("First row cell value: " + sheet.getRow(0).getCell(0).getStringCellValue());
System.out.println("Last row cell value: " + sheet.getRow(totalRows).getCell(0).getStringCellValue());
/*
Total rows: 5
First row cell value: 0
Last row cell value: 5
*/

Categories

Resources