PrimeFaces 3 dimensional DataTable - jsf

I have a 2D DataTable which I would like to insert a variable number of panels inside, essentially creating a 3D DataTable.
The 2D table looks as follows:
ll
<p:dataTable var="rowName" value="#{tableBean.rowNames}">
<p:column headerText="" styleClass="ui-widget-header">
<h:outputText value="#{rowName}"/>
</p:column>
<p:columns var="columnName" value="#{tableBean.colNames}" headerText="#{columnName}">
<p:panel>
<h:outputText value="panel"/>
</p:panel>
</p:columns>
</p:dataTable>
And Java:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import java.util.ArrayList;
import java.util.List;
#ManagedBean
#RequestScoped
public class TableBean {
private List<String> rowNames = new ArrayList<String>();
private List<String> colNames = new ArrayList<String>();
private ArrayList<ArrayList<ArrayList<String>>> data3D = new ArrayList<ArrayList<ArrayList<String>>>();
public TableBean() {
rowNames.add("row1");
rowNames.add("row2");
colNames.add("col1");
colNames.add("col2");
colNames.add("col3");
// Setup 3 dimensional structure
for (int i = 0; i < rowNames.size(); i++) {
data3D.add(new ArrayList<ArrayList<String>>());
for (int j = 0; j < colNames.size(); j++) {
data3D.get(i).add(new ArrayList<String>());
}
}
// row 1, col 1, 3 items
data3D.get(0).get(0).add("item1");
data3D.get(0).get(0).add("item2");
data3D.get(0).get(0).add("item3");
// row 1, col 2, 1 items
data3D.get(0).get(1).add("item1");
// row 1, col 3, 2 items
data3D.get(0).get(2).add("item1");
data3D.get(0).get(2).add("item2");
// row 2, col 1, 2 item
data3D.get(1).get(0).add("item1");
data3D.get(1).get(0).add("item2");
// row 2, col 2, 1 item
data3D.get(1).get(1).add("item1");
}
public List<String> getRowNames() { return rowNames; }
public void setRowNames(List<String> rowNames) { this.rowNames = rowNames; }
public List<String> getColNames() { return colNames; }
public void setColNames(List<String> colNames) { this.colNames = colNames; }
public ArrayList<ArrayList<ArrayList<String>>> getData3D() { return data3D; }
public void setData3D(ArrayList<ArrayList<ArrayList<String>>> data3D) { this.data3D = data3D; }
}
But I would like to achieve the following, where p1, p2, p3, etc are PrimeFaces panels:
+------+--------+--------+--------+--
| | Col1 | Col2 | Col3 | .... .... .... ....
+------+--------+--------+--------+--
| | +----+ | +----+ | +----+ |
| | | p1 | | | p1 | | | p1 | |
| | +----+ | +----+ | +----+ |
| Row1 | | p2 | | | | p2 | |
| | +----+ | | +----+ |
| | | p3 | | | |
| | +----+ | | |
+------+--------+--------+--------+
| | +----+ | +----+ | |
| | | p1 | | | p1 | | |
| Row2 | +----+ | +----+ | |
| | | p2 | | | |
| | +----+ | | |
+------+--------+--------+--------+
| .... |
| .... |
....
Is it possible to use the 3rd dimension (list of strings) to create a panel for each string, within each cell of the DataTable?

The solution as RongNK suggested is to use ui:repeat.
By using the brace notation [][], the array from the 3rd dimension can be specified. The index of the row and column can be accessed from the rowIndexVar and colIndexVar attributes from <p:dataTable> and <p:columns> respectively.
<p:dataTable var="rowName" value="#{tableBean.rowNames}" rowIndexVar="rowIdx">
<p:column headerText="" styleClass="ui-widget-header">
<h:outputText value="#{rowName}"/>
</p:column>
<p:columns var="columnName" value="#{tableBean.colNames}" headerText="#{columnName}"
columnIndexVar="colIdx">
<ui:repeat value="#{tableBean.data3D[rowIdx][colIdx]}" var="data">
<p:panel>
<h:outputText value="#{data}"/>
</p:panel>
</ui:repeat>
</p:columns>
</p:dataTable>

Related

How to partially split each row of a Primefaces datatable?

The problem I have is the following:
I have a datatable with a list of the class Producto, which internally consists of several fields of simple type and 2 objects of type Proveedor. Categoria and TipoProveedor are enums.
public class Producto {
private int idProducto;
private String nombre;
private Categoria categoria;
private Proveedor proveedorUno;
private Proveedor proveedorDos;
}
public class Proveedor {
private int idProveedor;
private TipoProveedor tipoProveedor;
private String nombre;
private BigDecimal precio;
}
My problem is that in each row of the datatable all the information must be displayed so that if a single filled provider comes, it will be displayed normal but if both fillings come, the part of the provider information is divided into 2 rows. Example:
|------------------------------------------------------------------|
| Nombre | Categoria | idProveedor | Nombre Proveedor | Precio |
|------------------------------------------------------------------|
| DIN-A4 | Papel | 10256 | Proveedor Manolito | 10.59 € |
| | | 24747 | Proveedor Fulanito | 10.75 € |
|------------------------------------------------------------------|
| DIN-A5 | Papel | 10256 | Proveedor Menganito | 4.77 € |
|------------------------------------------------------------------|
| DIN-A3 | Papel | 44787 | Proveedor Saturnino | 14.77 € |
|------------------------------------------------------------------|
I had tried to do it with a datatable like this but it does not look exactly as I need:
<p:dataTable id="listadoProductos" widgetVar="listadoProductos"
value="#{listadoProductosBean.listadoProductos}" var="producto"
emptyMessage="#{messages['tabla.VACIA']}">
<p:column headerText="#{messages['listado.NOMBRE']}">
<h:outputText value="#{producto.nombre}" />
</p:column>
<p:column headerText="#{messages['listado.CATEGORIA']}">
<h:outputText value="#{producto.categoria.desc}" />
</p:column>
<p:column headerText="#{messages['listado.ID_PROVEEDOR']}"
rendered="#{producto.proveedorUno != null && producto.proveedorDos != null}">
<p:panelGrid columns="1">
<p:row>
<h:outputText value="#{producto.proveedorUno.idProveedor}" />
</p:row>
<p:row>
<h:outputText value="#{producto.proveedorDos.idProveedor}" />
</p:row>
</p:panelGrid>
</p:column>
<p:column headerText="#{messages['listado.ID_PROVEEDOR']}"
rendered="#{producto.proveedorUno != null && producto.proveedorDos == null}">
<p:panelGrid columns="1">
<p:row>
<h:outputText value="#{producto.proveedorUno.idProveedor}" />
</p:row>
</p:panelGrid>
</p:column>
<p:column headerText="#{messages['listado.ID_PROVEEDOR']}"
rendered="#{producto.proveedorUno == null && producto.proveedorDos != null}">
<p:panelGrid columns="1">
<p:row>
<h:outputText value="#{producto.proveedorDos.idProveedor}" />
</p:row>
</p:panelGrid>
</p:column>
</p:dataTable>
Is it possible to do what I say?
My version of Primefaces is 6.2 but I could upgrade if I can get what I want.
Many Thanks.
NOTE: it could be worth it (although it is not desired) that the part of the providers divided into 2 appears even if there is only one. Example:
|------------------------------------------------------------------|
| Nombre | Categoria | idProveedor | Nombre Proveedor | precio |
|------------------------------------------------------------------|
| DIN-A4 | Papel | 10256 | Proveedor Manolito | 10.59 € |
| | | 24747 | Proveedor Fulanito | 10.75 € |
|------------------------------------------------------------------|
| DIN-A5 | Papel | 10256 | Proveedor Menganito | 4.77 € |
| | | | | |
|------------------------------------------------------------------|
| DIN-A3 | Papel | 44787 | Proveedor Saturnino | 14.77 € |
| | | | | |
|------------------------------------------------------------------|
You can use the groupRow attribute on the columns you want to group, so:
<p:column headerText="#{messages['listado.NOMBRE']}" groupRow="true">
<h:outputText value="#{producto.nombre}" />
</p:column>
<p:column headerText="#{messages['listado.ID_PROVEEDOR']}">
<h:outputText value="#{producto.proveedorUno.idProveedor}" />
</p:column>
See:
https://www.primefaces.org/showcase/ui/data/datatable/rowGroup.xhtml (rowspan)
https://primefaces.github.io/primefaces/8_0/#/components/datatable?id=row-grouping
To use a fixed row height, give your table a styleClass and style the table cells to have a min-height using CSS.
See:
How do I override default PrimeFaces CSS with custom styles?

Expand single row into multiple rows if next column has content in a spreadsheet

I have a tree structure defined in a Google spreadsheet. I need to modify the following structure:
+---+------------+-----------------+--------------------+-----+--------------+
| | A | B | C | D | E |
+---+------------+-----------------+--------------------+-----+--------------+
| 1 | Techniques | Adduction d'eau | Conduites en fonte | Eau | Tuyaux |
| 2 | | | | | Robinetterie |
| 3 | | | | | Outils |
| 4 | | | | | Accessoires |
| 5 | | | | Gaz | Tuyaux |
| 6 | | | | | Robinetterie |
| 7 | | | | | Outils |
| 8 | | | | | Accessoires |
+---+------------+-----------------+--------------------+-----+--------------+
into this:
+----+------------+-----------------+---------------------+-----+--------------+
| | A | B | C | D | E |
+----+------------+-----------------+---------------------+-----+--------------+
| 1 | Techniques | | | | |
| 2 | | Adduction d'eau | | | |
| 3 | | | Conduites en fonte | | |
| 4 | | | | Eau | |
| 5 | | | | | Tuyaux |
| 6 | | | | | Robinetterie |
| 7 | | | | | Outils |
| 8 | | | | | Accessoires |
| 9 | | | | Gaz | |
| 10 | | | | | Tuyaux |
| 11 | | | | | Robinetterie |
| 12 | | | | | Outils |
| 13 | | | | | Accessoires |
+----+------------+-----------------+---------------------+-----+--------------+
Any idea how to proceed? Thanks in advance
Here's my solution. Probably not perfect but it does the job.
var ss = SpreadsheetApp.getActiveSpreadsheet();
function onOpen() {
var menu = [{name:"Split in rows", functionName:"splitInRows"}];
ss.addMenu("Extras", menu);
}
function splitInRows() {
// Get the current sheet, row, and column.
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var range = sheet.getRange(1, 1, lastRow, lastColumn);
var numRows = range.getNumRows();
var numCols = range.getNumColumns();
for (var i = numRows; i > 0; i--) {
for (var j = numCols; j > 0; j--) {
var cell = range.getCell(i,j);
var cellValue = cell.getDisplayValue();
if ( j > 1 && cellValue ) {
// check if previous cell has content
var previousCellValue = range.getCell(i,j-1).getDisplayValue();
if (previousCellValue){
// create empty row
sheet.insertRowsAfter(i, 1);
// move cell value into cell just below
var currentCell = sheet.getRange(i, j);
var belowCell = sheet.getRange(i+1, j);
currentCell.copyTo(belowCell, {contentsOnly:true});
currentCell.clearContent();
}
}
}
}
}

How can I convert a input string into dictionary for each rows of a column in pyspark

I have a column values of a dataframe where I am receiving a string input like below where startIndex is the index of beginning of each character, end index is the end of occurrence of that character in the string and flag is the character itself.
+---+------------------+
| id| Values |
+---+------------------+
|01 | AABBBAA |
|02 | SSSAAAA |
+---+------------------+
Now I want to convert the string into dictionary for each rows as depicted below:
+---+--------------------+
| id| Values |
+---+--------------------+
|01 | [{"startIndex":0, |
| | "endIndex" : 1, |
| | "flag" : A }, |
| | {"startIndex":2, |
| | "endIndex" : 4, |
| | "flag" : B }, |
| | {"startIndex":5, |
| | "endIndex" : 6, |
| | "flag" : A }] |
|02 | [{"startIndex":0, |
| | "endIndex" : 2, |
| | "flag" : S }, |
| | {"startIndex":3, |
| | "endIndex" : 6, |
| | "flag" : A }] |
+---+--------------------+-
I have the pseudo code to frame the dictionary but not sure how to apply it
to all the rows at one go without using loops. Also the problem with such
approach is only the last framed dictionary is getting overwritten in all the rows
import re
x = "aaabbbbccaa"
xs = re.findall(r"((.)\2*)", x)
print(xs)
start = 0
output = ''
for item in xs:
end = start + (len(item[0])-1)
startIndex = start
endIndex = end
qualityFlag = item[1]
print(startIndex, endIndex, qualityFlag)
start = end+
Using udf() to wrap up the code logic and to_json() to convert the array of structs into string:
from pyspark.sql.functions import udf, to_json
import re
df = spark.createDataFrame([
('01', 'AABBBAA')
, ('02', 'SSSAAAA')
] , ['id', 'Values']
)
# argument `x` is a StringType() over the udf function
# return `row` as a list of dicts
#udf('array<struct<startIndex:long,endIndex:long,flag:string>>')
def set_fields(x):
row = []
for m in re.finditer(r'(.)\1*', x):
row.append({
'startIndex': m.start()
, 'endIndex': m.end()-1
, 'flag': m.group(1)
})
return row
df.select('id', to_json(set_fields('Values')).alias('Values')).show(truncate=False)
+---+----------------------------------------------------------------------------------------------------------------------------+
|id |Values |
+---+----------------------------------------------------------------------------------------------------------------------------+
|01 |[{"startIndex":0,"endIndex":1,"flag":"A"},{"startIndex":2,"endIndex":4,"flag":"B"},{"startIndex":5,"endIndex":6,"flag":"A"}]|
|02 |[{"startIndex":0,"endIndex":2,"flag":"S"},{"startIndex":3,"endIndex":6,"flag":"A"}] |
+---+----------------------------------------------------------------------------------------------------------------------------+

Getting a column as concatenated column from a reference table and primary id's from a Dataset

I'm trying to get a concatenated data as a single column using below datasets.
Sample DS:
val df = sc.parallelize(Seq(
("a", 1,2,3),
("b", 4,6,5)
)).toDF("value", "id1", "id2", "id3")
+-------+-----+-----+-----+
| value | id1 | id2 | id3 |
+-------+-----+-----+-----+
| a | 1 | 2 | 3 |
| b | 4 | 6 | 5 |
+-------+-----+-----+-----+
from the Reference Dataset
+----+----------+--------+
| id | descr | parent|
+----+----------+--------+
| 1 | apple | fruit |
| 2 | banana | fruit |
| 3 | cat | animal |
| 4 | dog | animal |
| 5 | elephant | animal |
| 6 | Flight | object |
+----+----------+--------+
val ref= sc.parallelize(Seq(
(1,"apple","fruit"),
(2,"banana","fruit"),
(3,"cat","animal"),
(4,"dog","animal"),
(5,"elephant","animal"),
(6,"Flight","object"),
)).toDF("id", "descr", "parent")
I am trying to get the below desired OutPut
+-----------------------+--------------------------+
| desc | parent |
+-----------------------+--------------------------+
| apple+banana+cat/M | fruit+fruit+animal/M |
| dog+Flight+elephant/M | animal+object+animal/M |
+-----------------------+--------------------------+
And also I need to concat only if(id2,id3) is not null. Otherwise only with id1.
I breaking my head for the solution.
Exploding the first dataframe df and joining to ref with followed by groupBy should work as you expected
val dfNew = df.withColumn("id", explode(array("id1", "id2", "id3")))
.select("id", "value")
ref.join(dfNew, Seq("id"))
.groupBy("value")
.agg(
concat_ws("+", collect_list("descr")) as "desc",
concat_ws("+", collect_list("parent")) as "parent"
)
.drop("value")
.show()
Output:
+-------------------+--------------------+
|desc |parent |
+-------------------+--------------------+
|Flight+elephant+dog|object+animal+animal|
|apple+cat+banana |fruit+animal+fruit |
+-------------------+--------------------+

Show nested datatable without repeating original data

I want to show the data from the database but can not repeat the same profile, just add a location to the existing profile. With the example above becomes clear:
+-------------------+---------------+---------------+
| PROFILE | LOCATION | STATUS |
+-------------------+---------------+---------------+
+-------------------+---------------+---------------+
| Admin | Chief | OK |
+-------------------+---------------+---------------+
| Director | Supervision | OK |
+-------------------+---------------+---------------+
| Secretary | Supervision | OK |
+-------------------+---------------+---------------+
| | Admin | OK |
| | Chief | OK |
| Chief-accessor | Director | OK |
| | Secretary | OK |
| | Supervision | OK |
+-------------------+---------------+---------------+
With this piece of code that is my datatable, I can only repeat them, but I can not group them:
<rich:panel header="User location info" style="margin: 20px 0 !important;">
<h:dataTable id="userLocationList"
value="#{userLocation.list()}" var="row"
styleClass="rich-table"
headerClass="rich-table-subheader rich-table-subheadercell rich-table-thead"
rowClasses="rich-table-row"
columnClasses="rich-table-cell,rich-table-cell,rich-table-cell">
<h:column>
<f:facet name="header">
<h:outputText value="Profile" />
</f:facet>
<center>
<h:outputText value="#{row.profile}" />
</center>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Location" />
</f:facet>
<h:outputText value="#{row.location}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Status" />
</f:facet>
<center>
<h:outputText value="#{row.profile.status ? 'OK' : 'Inactive'}" />
</center>
</h:column>
</h:dataTable>
</rich:panel>
Like this:
+-------------------+---------------+---------------+
| PROFILE | LOCATION | STATUS |
+-------------------+---------------+---------------+
+-------------------+---------------+---------------+
| Admin | Chief | OK |
+-------------------+---------------+---------------+
| Director | Supervision | OK |
+-------------------+---------------+---------------+
| Secretary | Supervision | OK |
+-------------------+---------------+---------------+
| Chief-accessor | Admin | OK |
+-------------------+---------------+---------------+
| Chief-accessor | Chief | OK |
+-------------------+---------------+---------------+
| Chief-accessor | Director | OK |
+-------------------+---------------+---------------+
| Chief-accessor | Secretary | OK |
+-------------------+---------------+---------------+
| Chief-accessor | Supervision | OK |
+-------------------+---------------+---------------+
Method list() from UserLocation is just a select in table_user_location:
select userLoc from UserLocation userLoc where userLoc.user.id = :userId
I've tried to follow some answers here in StackOverflow that say to put a datatable inside another datatable, but as I could was to repeat all locations without group profiles, example:
+-------------------+---------------+---------------+
| PROFILE | LOCATION | STATUS |
+-------------------+---------------+---------------+
+-------------------+---------------+---------------+
| | Chief | |
| | Supervision | |
| | Supervision | |
| | Admin | |
| Admin | Chief | OK |
| | Director | |
| | Secretary | |
| | Supervision | |
+-------------------+---------------+---------------+
| | Chief | |
| | Supervision | |
| | Supervision | |
| | Admin | |
| Director | Chief | OK |
| | Director | |
| | Secretary | |
| | Supervision | |
+-------------------+---------------+---------------+
| | Chief | |
| | Supervision | |
| | Supervision | |
| | Admin | |
| Secretary | Chief | OK |
| | Director | |
| | Secretary | |
| | Supervision | |
+-------------------+---------------+---------------+
| | Chief | |
| | Supervision | |
| | Supervision | |
| | Admin | |
| Chief-accessor | Chief | OK |
| | Director | |
| | Secretary | |
| | Supervision | |
+-------------------+---------------+---------------+
| | Chief | |
| | Supervision | |
| | Supervision | |
| | Admin | |
| Chief-accessor | Chief | OK |
| | Director | |
| | Secretary | |
| | Supervision | |
+-------------------+---------------+---------------+
And so it goes...
My doubt is the generic query I'm doing, so the duplicate content in the table. Does anyone have an idea for this operation?
Questions related:
Nested datatable in JSF 2.0
JSF: Empty nested dataTable
JSF, datatable in datatable
As I was not able to do it the way they return all locations for the same profile and using only one query, I decided to do differently, I thought of using two queries, one for all without repetition profiles (select distinct ...) and one for each location according to the profile. Only realized (correct me if I'm wrong) the property h:datatable renders by columns rather than rows. So, in part, was the first profile column returned all without repeating profiles, however the locations were the same in each cell was shown all user locations without distinction if the cell that the location would appear was of a given profile. That's when I realized I was very focused in locations where the focus was to be on user profiles.
Ran the select distinct the profiles and created a method to return the locations that profiles at the time the table was created. Just as the reported questions, a datatable inside another datatable.
Finally got my code:
<rich:panel header="Informations" style="margin: 20px 0 !important;">
<h:dataTable id="userLocationList"
value="#{userLocation.list()}" var="row"
styleClass="rich-table"
headerClass="rich-table-subheader rich-table-subheadercell rich-table-thead"
rowClasses="rich-table-row"
columnClasses="rich-table-cell,rich-table-cell,rich-table-cell">
<h:column>
<f:facet name="header">
<h:outputText value="Profile" />
</f:facet>
<center>
<h:outputText value="#{row.name}" />
</center>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Localizations" />
</f:facet>
<h:dataTable id="uLoc" var="c"
value="#{home.getLocByProfile(row.idProfile)}">
<h:column>
<h:outputText value="#{c}" />
</h:column>
</h:dataTable>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Status" />
</f:facet>
<center>
<h:outputText value="#{row.active ? 'OK' : 'Inactive'}" />
</center>
</h:column>
</h:dataTable>
</rich:panel>

Resources