I see examples on the internet for adding a row to a TableView, for example using the Person class in the Oracle documentation.
But I have a variable number of columns, so I can't bind to a Person (or any other) bean business object.
The Oracle example goes on to show how to bind columns to property names, but for that, it only shows how to add columns, but not rows.
My question is, can someone point me to a Hello, World example of dynamically adding arbitrary columns and/or rows to a JavaFX 8 TableView?
Use a List<String> (for example) for the data type, and just set the cell value factory as a callback that indexes into the list.
For example, this will create a TableView<List<String>> that is constructed out of an arbitrary tab-delimited text file. Not all rows in the file need have the same number of elements (it will pad with blanks). (It doesn't support escaped tabs, etc):
public TableView<List<String>> readTabDelimitedFileIntoTable(Path file) throws IOException {
TableView<List<String>> table = new TableView<>();
Files.lines(file).map(line -> line.split("\t")).forEach(values -> {
// Add extra columns if necessary:
for (int i = table.getColumns().size(); i < values.length; i++) {
TableColumn<List<String>, String> col = new TableColumn<>("Column "+(i+1));
col.setMinWidth(80);
final int colIndex = i ;
col.setCellValueFactory(data -> {
List<String> rowValues = data.getValue();
String cellValue ;
if (colIndex < rowValues.size()) {
cellValue = rowValues.get(colIndex);
} else {
cellValue = "" ;
}
return new ReadOnlyStringWrapper(cellValue);
});
table.getColumns().add(col);
}
// add row:
table.getItems().add(Arrays.asList(values));
});
return table ;
}
Kind of clunky, but this sample code seems to work:
TableView table = new TableView<>();
private char nextChar = 'A';
private void addColumn(TableView table) {
String mapChar = String.valueOf(nextChar++);
TableColumn<Map, String> column = new TableColumn<>("Class " + mapChar);
column.setCellValueFactory(new MapValueFactory(mapChar));
column.setMinWidth(130);
column.setCellFactory(cellFactoryForMap);
table.getColumns().add(column);
}
private void addRow(TableView table) {
ObservableList<Map> allData = table.getItems();
int offset = allData.size();
Map<String, String> dataRow = new HashMap<>();
for (int j = 0; j < table.getColumns().size(); j++) {
String mapKey = Character.toString((char) ('A' + j));
String value1 = mapKey + (offset + 1);
dataRow.put(mapKey, value1);
}
allData.add(dataRow);
}
}
Related
I am trying create a header from an excel file which has the table headers on two different rows. I have to make a single header row from those two.
The approach i am following is i am appending a # to the first header and trying to replace it with the second header value.
My excel file looks like this
Bank Positive Name of Local Approah
Value Customer Civilian Remote
//some data
And here is the code I use
public List<String> processSheet(Sheet sheet) {
ActivityLauncher.logConsoleMsg("Processing sheet " + sheet.getSheetName() + " started");
List<String> rows = new ArrayList<String>();
Iterator<Row> rowIterator = sheet.iterator();
List<String>firstHeader = new ArrayList<String>();
List<String>secondHeader = new ArrayList<String>();
List<String>finalHeader = new ArrayList<String>();
while (rowIterator.hasNext()) {
Row currentRow = rowIterator.next();
StringBuilder row = new StringBuilder();
for (int i = 0; i < currentRow.getLastCellNum(); i++) {
if(currentRow.getRowNum()==0 || currentRow.getRowNum()==1 || currentRow.getRowNum()==3 || currentRow.getRowNum()==2) {
continue;
} else {
Cell currentCell = currentRow.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String cellValue = excelManager.evalCell(currentCell);
//inserting the alternate row into the text file to make a continues header
if(currentCell.getRowIndex()==4 ) {
if(cellValue.equals("Local Risk")) {
row.append(cellValue);
row.append("#");
break;
} else {
row.append(cellValue);
row.append("#");
}
}
if(currentCell.getRowIndex()==5) {
if(cellValue.equals("rating")) {
row.append(cellValue);
row.append("#");
break;
} else {
int pos = row.indexOf("#");
if(pos >=0) {
row.replace(pos, 1, cellValue);
}
}
}
}
}
if (!row.toString().isEmpty()) {
rows.add(row.toString());
}
}
ActivityLauncher.logConsoleMsg("Processing sheet " + sheet.getSheetName() + " completed");
return rows;
}
Currently, I get this output :
Bank#Positive#Name of#Local Approah#
Value Customer Civilian Remote
But my aim is to make
Bank Value Positive Customer Name of Civilian Approach Remote
Can somebody help me solve this?
row.toString().replaceAll("#", cellValue); has no effect, because the string that you get after the call of toString and subsequent replacement gets dropped and garbage collected.
If you want to do an in-place replacement, use indexOf and replace(int,int,String):
int pos = row.indexOf("#");
if (pos >= 0) {
row.replace(pos, pos+1, cellValue);
}
In addition, the replacement has to happen when you are scanning a different row from the insertions of '#' characters, so you need to make sure that StringBuilder from row 4 "survives" when you go into row 5. For this to work declare row outside the loop, and replace it with a new one only when the current row is not equal to 4:
StringBuilder row = new StringBuilder();
while (rowIterator.hasNext()) {
Row currentRow = rowIterator.next();
...
if (currentCell.getRowIndex() == 4) {
continue;
}
if (!row.toString().isEmpty()) {
rows.add(row.toString());
row = new StringBuilder();
}
}
External link for a fiddle :
Here is a fiddle I made with hard-coded data that uses pretty much the same algorithm than the bellow examples.
For the header, you would better make a list with the two rows and append the strings when you are done.
Try this code instead :
public List<String> processSheet(Sheet sheet) {
List<String> headers = new ArrayList<>();
Iterator<Row> rowIterator = sheet.iterator();
int rowCnt = 0;
while (rowIterator.hasNext()) {
Row currentRow = rowIterator.next();
for (int i = 0; i < currentRow.getLastCellNum(); ++i) {
Cell currentCell = currentRow.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String cellValue = excelManager.evalCell(currentCell);
headers.add(rowCnt + (i * (rowCnt+1)), cellValue);
}
++rowCnt;
}
return headers;
}
This algorithm will work for any number of rows and any number of columns, as long as the number of columns per row is the same. here's a fiddle with hard-coded example data that follows the same algorithm. When this is done, you can append the strings 2 by 2 (or 3 by 3 if the headers are 3 rows high). Something like this algorithm should be alright for that :
private static List<String> appendStrings(List<String> original, int group) throws Exception {
if (original.size() % group == 0) {
List<String> newList = new ArrayList<>();
for (int i = 0; i < original.size(); i += group) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < group; ++j) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(original.get(i + j));
}
newList.add(sb.toString());
}
return newList;
} else {
throw new Exception("MyClass::appendStrings() ---> the group value must be a factor of the size of the original list");
}
}
If your rows may contain empty cells and you don't want the empty string to be added, but still want to respect the order, you will need to remove the data after the list is generated. Like this :
public List<String> processSheet(Sheet sheet) {
/*... same code as previous example ...*/
// the following removes all "" or null values
while(headers.remove(null) || headers.remove("")){}
return headers;
}
I want to retrieve data from database column wise. Say if I have Column 'a' then I want all values of that column and then I have another column say 'b' then I want to get all values of that column and so on for all columns.
My code is giving me row wise value. First it will retrieve data from first row and then second and so on. My code is as follows.
while (rs.next()) {
for(inti=1;i<=rsMetaData.getColumnCount();i++) {
System.out.println(rs.getString(i));
}
}
Please suggest how to get all values of first column then second and so on.
Iterate Data Row wise and convert to Column wise. Something Like this :
ResultSet rs = con.createStatement().executeQuery("SELECT * FROM table");
ResultSetMetaData rsMetaData = rs.getMetaData();
Map<String, List<Object>> resultMap = new LinkedHashMap<>();
while (rs.next()) {
for(int i=1;i<=rsMetaData.getColumnCount();i++) {
String strColumnName = rsMetaData.getColumnName(i);
Object columnValue = rs.getObject(i);
if(resultMap.containsKey(strColumnName)){
resultMap.get(strColumnName).add(columnValue);
}else{
List<Object> resultList = new ArrayList<>();
resultList.add(columnValue);
resultMap.put(strColumnName,resultList);
}
}
}
// Iterate Data Column Wise
for(Iterator<Map.Entry<String, List<Object>>> iterator = resultMap.entrySet().iterator();iterator.hasNext();){
Map.Entry<String, List<Object>> entry = iterator.next();
System.out.println("Column Name: "+entry.getKey());
System.out.println("Column Values: "+entry.getValue());
}
A more flexible solution might use a HashMap instead of a List. This way you can store the column names as keys, which makes retrieving values easier and more transparent.
//declare a HashMap
Map<String,Object> hMap = new HashMap<String,Object>();
public void populateColumns(ResultSet rs) {
try{
while(rs.next()){
for (int i=1;i<=rs.getMetaData().getColumnCount();i++) {
Object obj=rs.getObject(i); //get the value for whatever column the result has
hMap.put(rs.getMetaData().getColumnName(i), obj);
} }
}catch (Exception e) {
//handle the exception
}
}
Note that if you want special handling for some database types /Java types, you can use the instanceof operator.
for (int i=1;i<=rs.getMetaData().getColumnCount();i++) {
Object obj=rs.getObject(i); /
if(obj instanceof String) //if obj is a String
hMap.put(rs.getMetaData().getColumnName(i), new String(((String)obj).getBytes("UTF-8"),"UTF-8"));
else //for every other objects
hMap.put(rs.getMetaData().getColumnName(i), obj);
UPDATE:
To get the output like you want try this
public void populateColumns(ResultSet rs) {
List<Object> list = new ArrayList<Object>();
int colCount;
try{
while(rs.next()){
colCount = rs.getMetaData().getColumnCount();
for (int i=1;i<=colCount;i++) {
Object obj=rs.getObject(i); //get the value for whatever column the result has
list.add(obj);
}
}
}catch (Exception e) {
//handle the exception
}
//
List<Object> sortedByCol = new ArrayList<Object>();
for(int j=0; j<colCount;j++){
for(int i=j; i<list.size(); i+=colCount){
sortedByCol.add(list.get(i));
}
}
For input like
col a col b col c
1 row 1 2 3
2 row 1 2 3
your output list (sortedByCol) will have
1, 1, 2, 2, 3, 3
have comma delimitered String value for each column which is in an ArraList
Something like (Sorry not in front of a computer)
ArrayList<String> columnValList = new ArrayList<>();
while (rs.next()) {
for(inti=0;i<rsMetaData.getColumnCount();i++) {
// get element for this col - create if doesnt exist
String cols = columnValList.get (i); // error checking needed
cols = cols + "," + rs.getString (i + 1);
// put it back
columnValList.set (i, cols);
}
}
after this you will have element(0) containing all values for the first columns etc
I have two tables from the same excel in a different sheets. In table 1: There are two columns like "Actual value" and "Saved Value" The cell values of two columns are looks like this Actual value : row(0)2005.99, row(1)6774.00... Saved value : row(0)ClaimsTotal, row(1)ClaimsAServiceTotal
In table 2: There is an column name called "Calculations" "Value1" "Value2" "Value3" The cell values of Calculations column looks like this Calculations: row(0)ClaimsTotal + ClaimsAServiceTotal = ClaimsGrandTotal ......etc
I have written the code for excel reading and writing, below are the methods for the reference
ExcelReader excelutil = new ExcelReader("Guardian Role");
Object[] savedValue = excelutil.readExcelByColName("Saved Value");
Object[] actualValue = excelutil.readExcelByColName("Actual Value");
ExcelReader readCaliTable = new ExcelReader("Data Verification");
Object[] caliculation = readCaliTable.readExcelByColName("Calculations");
My requirement is: First I need to split table 2 Calculations column cell value and need to keep in two different strings like value[0] = ClaimsTotal and value1=ClaimsAServiceTotal. based on this cell string I want to check this in table 1 Saved Value column, If it is present then corresponding value from table 1 Actual Value column need to get and do calculation Ex:
ClaimsTotal = 2005.99 (I need to print this in table 2 value 1)
ClaimsAServiceTotal= 6774.00 (I need to print this in table 2 value 2)
(2005.99+6774.00)= total (I need to print this in table 2 value 3) like wise in some rows there will be +,=
public ExcelReader(String sheetname, String sheetname1) throws IOException {
super();
this.filename = configProps.getProperty("testData");;
this.sheetname = sheetname;
this.sheetname1 = sheetname1;
this.excelFile = new FileInputStream(new File(this.filename));
this.workbook = new XSSFWorkbook(this.excelFile);
this.sheet = workbook.getSheet(this.sheetname);
this.sheet1 = workbook.getSheet(this.sheetname1);
}
public void verifyData(String colName) throws FileNotFoundException {
Object[] calc = this.readExcelByColName(colName);
for (int i = 0; i < calc.length; i++) {
String string = (String) calc[i];
string = string.replaceAll("\\s", "");
String[] parts = string.split("(\\+)|(\\=)");
int j = 1;
for (String s : parts) {
int[] index1 = findIndex(this.sheet1, s);
int[] index2 = findIndex(this.sheet, string);
this.sheet.getRow(index2[0]).getCell((index2[1] + j))
.setCellValue(this.sheet1.getRow(index1[0]).getCell(index1[1] - 1).getStringCellValue());
j++;
}
}
this.outFile = new FileOutputStream(new File(this.filename));
}
private int[] findIndex(Sheet sheet, String cellContent) {
for (Row row : sheet) {
for (Cell cell : row) {
if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
if (cell.getStringCellValue().replaceAll("\\s", "").equals(cellContent)) {
int[] index = { row.getRowNum(), cell.getColumnIndex() };
return index;
}
}
}
}
return null;
}
main method#Test
ExcelReader excelUtil2 = new ExcelReader("Verify Data","Guardian Angel Hospice");
excelUtil2.verifyData("Calculations");
Object[] data2 = excelUtil2.readExcelByColName("Value1");
for (int i = 0; i < data2.length; i++) {
System.out.println(data2[i]);
}
excelUtil2.saveAndCloseExcel();
PFA Excel
I have a code where I am getting data when I input values say itr.get(0),str.get(0)etc... But I want to create a for loop to it but I cannot use it since its inside model.addRow
And also each one is of different size array list object(itr,str,dub).
How do I input data through for loop to it so I don't have to call it manually.
public Data1()
{
super();
setDefaultCloseOperation(EXIT_ON_CLOSE);
JTable table = new JTable(new DefaultTableModel(new Object[]{"Integers", "RealNumbers","OtherTokens"},5));
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.addRow(new Object[]{itr.get(0),dub.get(0) ,str.get(0) });
model.addRow(new Object[]{itr.get(1),dub.get(1) ,str.get(1) });
model.addRow(new Object[]{itr.get(2),dub.get(2) ,str.get(2) });
model.addRow(new Object[]{itr.get(3), "" ,str.get(3) });
model.addRow(new Object[]{itr.get(4), "" ,str.get(4) });
model.addRow(new Object[]{"", "" ,str.get(5) });
table.setPreferredScrollableViewportSize(new Dimension(500,80));
JScrollPane pane = new JScrollPane(table);
getContentPane().add(pane,BorderLayout.CENTER);
}
The original question asked about adding in a loop to a table. However, the real problem is not the loop per se but rather the fact that there are a different number of elements of different types. This answer takes some data that was presented in the chat, and puts it into arrays. It could be read out of a file. It solves the question of what to put in a given row when there is no data by placing an empty String in the array.
The approach is to use the TableModel rather than attempting to add in a single shot. However, one could construct the necessary array if desired and pass it to the constructor instead. However, the TableModel is a better approach in the long run, IMHO.
public static void main(String[] args)
{
// these three arrays represent the test data otherwise read
// from a file
int[] ia = { 1493, -832722, 0, 1, 162 };
double[] da = { 0.4, -6.382, 9.0E-21 };
String[] sa = { "The", "fox", "jumped", "over", "the", "dog!"};
Object[] columnNames = { "Int", "Real", "Tokens" };
DefaultTableModel dm = new DefaultTableModel(columnNames, 0);
JTable tbl = new JTable(dm);
// while reading for a file, would know the max length in
// a different way
int loopCtr = Math.max(ia.length, da.length);
loopCtr = Math.max(loopCtr, sa.length);
// loop for the longest entry; for each entry decide if there
// is a value
for (int i = 0; i < loopCtr; ++i) {
Integer iv = (i < ia.length ? ia[i] : null);
Double dv = (i < da.length ? da[i] : null);
String sv = (i < sa.length ? sa[i] : "");
//add the row; if no value for a given entry, use an empty
// String
dm.addRow(new Object[]{(iv != null ? iv : ""),
(dv != null ? dv : ""),
sv});
}
//just output for the moment
int cols = dm.getColumnCount();
int rows = dm.getRowCount();
StringBuilder sb = new StringBuilder();
for (int row = 0; row < rows; ++row) {
sb.setLength(0);
for (int col = 0; col < cols; ++col) {
sb.append(dm.getValueAt(row, col));
sb.append("\t");
}
System.out.println(sb.toString());
}
}
The output demonstrates a table with blanks as needed.
1493 0.4 The
-832722 -6.382 fox
0 9.0E-21 jumped
1 over
162 the
dog!
This question already exists:
Extracting values which are not common in two columns of excel file using apache poi
Closed 8 years ago.
I am using the apache poi for reading and writing values to excel file with java. Now suppose I have three columns and three rows, i have values as
{row1, column1} = data1
{row1, column2} = data1
{row2, column1} = data2
{row2, column2} = data4
{row3, column1} = data3
{row3, column2} = data2
Now, I have stored all these values in two strings 'a' and 'b' for column 1 and column 2 respectively. I need to output which are common in both columns with following code:
for(int i=2; i<=rows_count;i++)
{
String a = datatable1.getCellData("temp", 1, i );
//System.out.println("a is " + a);
String b = datatable1.getCellData("temp", 3, i);
//System.out.println("b is " + b);
if(a.equals(b))
{
System.out.println(b);
}
}
With this I am getting the output as 'data1' only but not 'data2' as these are in different rows. Any ideas how to resolve this. Thanks!
I believe what you're asking is how to report all cell values which appear more than once in a section of the file. Assuming so, simplest thing is probably
Set<String> alreadySeen = new HashSet<String>();
Set<String> duplicates = new HashSet<String>();
DataFormatter fmt = new DataFormatter();
// Update these as per your requirements
int firstRowToCheck = 0;
int lastRowToCheck = Math.min(3, sheet.getLastRowNum());
int firstColumnToCheck = 0;
int lastColumnToCheck = 1;
// Loop over the rows and cells of interest
for (int rn=firstRowToCheck; rn <= lastRowToCheck; rn++) {
Row r = sheet.getRow(rn);
if (r == null) {
// No cells in this row have any values
} else {
for (int cn=firstColumnToCheck; cn <= lastColumnToCheck; cn++) {
Cell c = row.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// No value for this cell
} else {
String value = fmt.formatCellValue(c);
if (alreadySeen.contains(value)) {
// Duplicate!
duplicates.put(value);
} else {
// First time this has been seen
alreadySeen.put(value);
}
}
}
}
}
// Report duplicates
for (String dup : duplicates) {
System.out.println(dup);
}