Rowspan or nested? Create a table itextsharp - formatting

I've been searching about how to merge two cells and I've found two answers Rowspan and Nested. I can't make my table with those two functions because I don't know how to merge cells at the begining and at the end. I've been trying many ways but this is driving me crazy.
The table I want to make has 9 columns and 3 rows. So, I want the table looks like:
| Header 1 | Header 2 | Header 3 | Header 4 | Header 5 |
| | | H1 | H2 | H3 | H4 | H5 | | |
| D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 | D9 |
Header 1: cs=1, rs=2.
Header 2: cs=1, rs=2.
Header 3: cs=5, rs=1.
H1: cs=1, rs=1.
H2: cs=1, rs=1.
H3: cs=1, rs=1.
H4: cs=1, rs=1.
H5: cs=1, rs=1.
Header 4: cs=1, rs=2.
Header 5: cs=1, rs=2.
cs:colspan, rs:rowspan.
Header 3 contains H1, H2, H3, H4, H5.
I think the solution is pretty easy but I can't find it. I hope you can understand Which the problem is because I can't upload images yet.

There are a couple ways to do what you want to do. Since your table structure isn't overly complex perhaps the easiest approach is to create your PdfPTable with nine columns and add your cells one at a time using each cell's Rowspan and Colspan properties as you create rows.
The code below will create a table laid out exactly as you described:
private void CreatePdf() {
using (FileStream fs = new FileStream("TableTest.pdf", FileMode.Create)) {
Document doc = new Document(new iTextSharp.text.Rectangle(800f, 800f));
PdfWriter.GetInstance(doc, fs);
doc.Open();
PdfPTable table = new PdfPTable(9);
// Hdeaer row.
table.AddCell(GetCell("Header 1", 1, 2));
table.AddCell(GetCell("Header 2", 1, 2));
table.AddCell(GetCell("Header 3", 5, 1));
table.AddCell(GetCell("Header 4", 1, 2));
table.AddCell(GetCell("Header 5", 1, 2));
// Inner middle row.
table.AddCell(GetCell("H1"));
table.AddCell(GetCell("H2"));
table.AddCell(GetCell("H3"));
table.AddCell(GetCell("H4"));
table.AddCell(GetCell("H5"));
// Bottom row.
table.AddCell(GetCell("D1"));
table.AddCell(GetCell("D2"));
table.AddCell(GetCell("D3"));
table.AddCell(GetCell("D4"));
table.AddCell(GetCell("D5"));
table.AddCell(GetCell("D6"));
table.AddCell(GetCell("D7"));
table.AddCell(GetCell("D8"));
table.AddCell(GetCell("D9"));
doc.Add(table);
doc.Close();
}
}
private PdfPCell GetCell(string text) {
return GetCell(text, 1, 1);
}
private PdfPCell GetCell(string text, int colSpan, int rowSpan ) {
PdfPCell cell = new PdfPCell(new Phrase(text));
cell.HorizontalAlignment = 1;
cell.Rowspan = rowSpan;
cell.Colspan = colSpan;
return cell;
}
As you can see I've wrapped the construction of PdfPCells in helper methods that allow you to set each cell's text content and Rowspan and Colspan properties.
If you haven't already done so, I suggest you check out this tutorial on iTextSharp tables:
iTextSharp - Introducing Tables

Robert (below) is right! Rowspan is not supported in the latest versions of iTextSharp. Only colspan is supported. The URL provided above does state that it has been supported since v 2.1.6, but looks like that hasn't been updated in a while as there is no more setRowSpan() method in any of the new versions (above v5) and the RowSpan property setting is just ignored.
You're better off adding a empty cells for rowspanned cells.

Rowspan isn't a answer, because there is no rowspan in iTextSharp. You have to use nested tables.
Colspan is posible but not rowspan.
For your table, i would only use one colspan and use empty Cells between Header 1 and D1

Related

Google Sheets - Parse unsorted comma separated values from one column, into separate columns. The values are not always in the same order

Three example rows from that column are shown below.
{'gender': 'Female', 'document_type': 'driving_licence', 'date_of_expiry': '2024-03-03', 'issuing_country': 'GBR'}
{'nationality': 'DEU', 'document_type': 'national_identity_card', 'date_of_expiry': '2020-11-19', 'issuing_country': 'DEU'}
{'gender': 'Female', 'nationality': 'FRA', 'document_type': 'passport', 'date_of_expiry': '2024-01-22', 'issuing_country': 'FRA'}
My desired outcome would be:
gender | document_type | document_type | date_of_expiry | issuing country | nationality | national_identity_card |
e.g.
Female | driving_licence | 2024-03-03 | GBR | NULL | NULL
NULL | national_identity_card | 2020-11-19 | DEU | DEU
.
.
.
Any help would be great :)
Indeed it looks like JSON. You could replace all the ' with " and paste the records here: https://codebeautify.org/jsonviewer
The complete string for the examples you provided would be:
{
"example": [
{
"gender": "Female",
"document_type": "driving_licence",
"date_of_expiry": "2024-03-03",
"issuing_country": "GBR"
},
{
"nationality": "DEU",
"document_type": "national_identity_card",
"date_of_expiry": "2020-11-19",
"issuing_country": "DEU"
},
{
"gender": "Female",
"nationality": "FRA",
"document_type": "passport",
"date_of_expiry": "2024-01-22",
"issuing_country": "FRA"
}
]
}
After that, clicking the button JSON to CSV generates the values sorted by columns like this:
gender,document_type,date_of_expiry,issuing_country
Female,driving_licence,2024-03-03,GBR
,national_identity_card,2020-11-19,DEU
Female,passport,2024-01-22,FRA
While the answer from #jota seems to have solved your problem, I have an alternative solution for that works mostly automatically, though it depends if you always receive the data as objects, ideally in an array.
A potential Apps Script solution
First you need to create a Google Sheet and fill in the headers. The text must match exactly the title of the property titles you are receiving. For example, document_type not Document Type.
Then open the Script Editor by clicking on the 'Tools' menu, then select 'Script editor...'.
Replace the code with the following:
function addToSheet() {
// Ideally this data gets created automatically but here is the example you gave.
let example = [
{
gender: "Female",
document_type: "driving_licence",
date_of_expiry: "2024-03-03",
issuing_country: "GBR",
},
{
nationality: "DEU",
document_type: "national_identity_card",
date_of_expiry: "2020-11-19",
issuing_country: "DEU",
},
{
gender: "Female",
nationality: "FRA",
document_type: "passport",
date_of_expiry: "2024-01-22",
issuing_country: "FRA",
},
];
// Defining where to put the data
let ss = SpreadsheetApp.getActiveSpreadsheet(); // Spreadsheet the script is attached to
let sheet = ss.getSheetByName("Sheet1"); // Put the name of your sheet here
let sheetData = sheet.getDataRange().getValues(); // Gets all data from sheet.
// Take headers from data
let headers = sheetData.shift(); // removes first row, which is the headers, of data and assigns to variable
// For each entry, create an array, and fill it according to the header structure.
example.forEach((entry) => {
let newRow = []; // initializing new array
// build the new row according to the header structure.
headers.forEach(header => {
newRow.push(entry[header]); // add cell value to row, if not present in entry, will fill with undefined
})
sheet.appendRow(newRow); // add built row to sheet
});
}
I adapted the structure provided by #jota. What would be up to you is to adapt the data you are getting to be an array of objects. What I have defined as example, would be the necessary format.
Then if you run the code, it will append the rows in the right place.
References and explanation
SpreadsheetApp - the service within Apps Script that allows you to manipulate spreadsheets.
Spreadsheet - the spreadsheet object.
Sheet - the sheet object, in the example above, the sheet must be named "Sheet1" - though you can change that in your code to adapt to your needs, of course.
Range - The methods associated with ranges (selections etc).
Append Row - The method used to add the row at the end.
getDataRange - The method used to select the range containing all the data in the particular sheet. In this case, it is used to dynamically get the headers.
getValues - Used in conjunction with getDataRange to actually return the values. getDataRange only returns the range object which contains the values, but has a lot more to it too. With getValues you get a simple two dimensional array.
The script gets the headers in an array, it uses the headers array to build a row for the sheet for each object defined in example, and appends each row to the sheet after its built.
This can be adapted to many situations, but the advantage it has, is that the object can have all or none of the fields, in any order, and it will update the spreadsheet correctly regardless. As mentioned previously, the only issue you may encounter, is getting the initial data into the right format.

Google Spreadsheets API - Conditional Formatting based on range and adjacent values

I have the following very streamlined version of a spreadsheet:
Sect | Lbl | A | B | C | D | E
==========================================================
Sec1 | Lbl1 | 1 | 8 | 6 | 10 |
----------------------------------------------------------
Sec2 | Lbl2 | 2 | 1 | 1 | >100 |
----------------------------------------------------------
etc...
I want to apply a rule/rules across all values to say:
Bg Color = Green if:
- the cell to the right is not blank and higher than this value
Bg Color = Red if:
- the cell to the right is not blank and less than this value
Bg Color = white (no action) if:
- the cell to the right has the same value
In addition, if the value is set to the non-numeric ">100" I need to convert it to 100 as part of this formatting.
I'm using C# to do this with the Spreadsheets v4 API.
So far I have the code below but I'm unsure as to how I could apply multiple conditions to a formatting rule.
Update
Please note updated table example above before reading below
Thanks to TheMaster I've got something up and running but it's not quite right yet. I've got an additional factor of:
the first row should be excluded from the conditional formatting
the first two columns are labels and should also be ignored/excluded
all other columns and rows of data need to be compared to the column to their right (if available) and coloured accordingly.
Here is my code so far for the Red rule (where cell value > cell value to the right).
In addition to this I have a Green rule (where cell value < cell value to the right) and a White rule (where cell value = cell value to the right)
They are indexed as below in a batchupdate request:
0 = red
1 = green
2 = white
The Code for the Red rule:
formatRequest.Requests.Add(new Google.Apis.Sheets.v4.Data.Request()
{
AddConditionalFormatRule = new AddConditionalFormatRuleRequest()
{
Rule = new ConditionalFormatRule()
{
BooleanRule = new BooleanRule()
{
Condition = new BooleanCondition()
{
Type = "CUSTOM_FORMULA",
Values = new List<ConditionValue>() {
new ConditionValue()
{
UserEnteredValue = "=AND(NOT(ISBLANK(A2)),(1*REGEXEXTRACT(A2,\"\\d+\"))>(1*REGEXEXTRACT(B2,\"\\d+\")))"
}
}
},
Format = new CellFormat()
{
BackgroundColor = new Color()
{
Red = 0.8f,
Green = 0f,
Blue = 0f,
Alpha = 1f
}
}
},
Ranges = new List<GridRange>()
{
new GridRange()
{
SheetId = Convert.ToInt32(sheetId)
,StartRowIndex = 1
},
}
},
Index = 0
}
});
The problem is it doesn't apply the conditional formatting to the whole sheet...only the first column of data.
You should use CUSTOM_FORMULA as Boolean condition Type
You'll need to add two conditional formatting rules with index 0 and 1
Range will be open-ended and cover the full sheet.
Snippet(for A1:Z;Bg:red):
Boolean Condition JSON:
{
"type": "CUSTOM_FORMULA",
"values": [
{
userEnteredValue: "=AND(NOT(ISBLANK(A1)),A1>(IF(ISNUMBER(B1),B1,1*REGEXEXTRACT(B1,\"\d+\"))))"
}
]
}
BooleanRule/Format/BackgroundColor JSON:
{
"red": 1,
"green": 0,
"blue": 0,
"alpha": 1
}

How to calculate a field based on two columns while looping through a spreadsheet

I have a table with two columns, and I want to calculate the third.
Column 1 | Column 2 | Calculated Column
1 | 1 | 1
1 | 1.5 | 2
1 | 2 | 3
2 | 2 | 1
I am trying to add the third column to my sheet and then in ascending order increment the calculate value.. Once the value in column 1 changes, the incrementing should start over. I was good at this once.. but rusty :) Any help would be awesome
//name the datasets
var ss=SpreadsheetApp.getActiveSpreadsheet();
//add a new column and name it
ss.insertColumnAfter(2);
ss.getRange("C1").setValue("Tier");
//Get the first store number and result space number
var atvStr = ss.getRange("A2").getValue();
var strNbrR = ss.getRange("A2:A9000").getValues();
var rslt = ss.getRange("C2:C9000").getValues();
//get the last filled row and +1 to go on to the next
var line=ss.getLastRow()+1;
for (i in strNbrR) {
if (strNbrR[i] = atvStr) {
ss.getRange(line).setValue("1");
}
}
}
If I understand you correctly, try this:
function testMe(){
//name the datasets
var ss=SpreadsheetApp.getActiveSpreadsheet();
//add a new column and name it
ss.insertColumnAfter(2);
//Get the first store number and result space number
var strNbrR = ss.getRange("A2:A").getValues();
var rsltRng = ss.getRange("C2:C");
var rsltVals = rsltRng.getValues();
var newValue = 1;
for ( i in strNbrR) {
if (i >1 ){
if (strNbrR[i][0] !== strNbrR[i - 1][0]) {
++newValue;
}
}
rsltVals[i][0] = newValue;
}
rsltRng.setValues(rsltVals);
}
It will increment the value placed in Column C if the value in A is not the same as the value in cell A in the previous row.

Finding index using switch case statement in javascript

I'm using Pentaho(ETL) tool to achieve the output using a javascript component which accepts javascript code to achieve the desired transformation.The following table is imported into pentaho from a .csv file(source file).
For example this is my table structure
+--------+--------+--------+
| RLD | MD | INC |
+--------+--------+--------+
| 0 | 3868 | 302024 |
| 53454 | 7699 | 203719 |
| 154508 | 932 | 47694 |
| 107547 | 36168 | 83592 |
I want to use a script which would give me the max_value and its index number, such that my output would look like
Output Table
+--------+--------+--------+-----------+-----------+
| RQD | MT | IZC | max_value | max_index |
+--------+--------+--------+-----------+-----------+
| 0 | 3868 | 302024 | 302024 | 3 |
| 53454 | 7699 | 203719 | 203719 | 3 |
| 154508 | 932 | 47694 | 154508 | 1 |
| 456 | 107547| 83592 | 107547 | 2 |
To get the max value from rows I have used
var max_value = Math.max(RQD,MT,IZC);
println(max_value);
I tried to get their index using the following script
var max_index = switch (Math.max(RQD,MT,IZC))
{
case "RQD":document.write("1")
case "MT":document.write("2")
case "MT":document.write("3")
default:document.write("0")
}
How can I get the desired result in the form of javascript data structure? Any help would be much appreciated.Thanks
var list = [
{RLD:0,
MD:3868,
INC:302024
},
{RLD:53454,
MD:7699,
INC:203719
},
{RLD:154508,
MD:932,
INC:47694
},
{RLD:107547,
MD:36168,
INC:83592
},
];
list = list.map(function(item){
var keys = Object.keys(item);
item.max_value = item[keys[0]];
item.max_index = '';
for(var i = 1, l = keys.length; i < l; i++) {
var key = keys[i];
var keyValue = item[key];
if (item.max_value < keyValue){
item.max_value = keyValue;
item.max_index = key;
}
}
return item;
})
There are several issues with your code, lets solve them!
Use breaks: you must use breaks in order to avoid the switch moving to cases below its match.
switch cases do not return a value like functions, you cannot use a switch return to define a variable, you need to define the variable inside the switch case.
Math.max does not return the name of its maximum variable, instead it returns the maximum number from its given parameters.
to solve this issue, i would not use a switch case with math.max to be honest, however to answer your question:
var tableArray = [RQD,MT,IZC];
var maxIndex = tableArray.indexOf(Math.max.apply(null, arr));
if(maxIndex > -1) document.write(maxIndex+1);
i used +1 because you have your index in the example table starting from 1 instead of 0.
the way the array is sorted should match the way the table is sorted per raw.
First of all you could not solve this problem with a switch statement.
In a javascript switch you should provide a value that is one of the followed case(s), otherwise the switch will go to the default if defined.
Your problem seems to be to find out the higher value of 3 columns and print out, the colums row by row adding a column with the max value and the index of the column where you found it.
So for example on the row:
1, RLD : 0
2, MD : 3868
3, INC : 302024
In this case the higher value is INC with the column index 3.
If you have just the variables with the number values, you could do nothing more than something like this:
function getMaxValueRow (RLD, MD, INC) {
var max_value = RLD;
var max_index = 1;
if (MD > max_value) {
max_value = MD;
max_index = 2;
}
if (INC > max_value) {
max_value = INC;
max_index = 3;
}
return [RLD, MD, INC, max_value, max_index];
}
You could return an object too like this:
retrun {
'RQD': RLD,
'MT': MD,
'IZC': INC,
'max_value': max_value,
'max_index': max_index
}

Convert a datatable to a data structure to be read by MS stacked chart control?

I have a chart control (stacked chart to be more precise) and a datatable that contains 3 columns. I want to bind them into a stacked bar chart. below is what the datatable looks like:
Resource | Queue | Hoursworked
Billy Jones | Projects - Internal | 234
Billy Jones | Tier 1 Support | 234
Alan Clark | Projects - Internal | 123
Alan Clark | Tier 1 Support | 234
I would have the Resource column as the x-axis, and the value (hours worked) of each Queue as the y-axis (stacked)
Can anyone help? I've tried just binding normally and I'm getting an error saying that it's the wrong data type.
Thanks,
Billy
Try this out for size:
DataTable table2 = new DataTable();
table2.Columns.Add("Resource", typeof(string));
table2.Columns.Add("Queue", typeof(string));
table2.Columns.Add("Hoursworked", typeof(int));
table2.Rows.Add("Billy Jones", "Projects - Internal", 234);
table2.Rows.Add("Billy Jones", "Tier 1 Support" , 234);
table2.Rows.Add("Alan Clark", "Projects - Internal" ,123);
table2.Rows.Add("Alan Clark", "Tier 1 Support", 234);
foreach (DataRow row in table2.Rows)
{
string seriesName1 = row["Queue"].ToString();
Series series = new Series(); ;
try {
series = Chart2.Series[seriesName1];
}
catch {
if (series.Name == "") {
Chart2.Series.Add(seriesName1);
Chart2.Series[seriesName1].ChartType = SeriesChartType.StackedColumn;
}
}
Chart2.Series[row["Queue"].ToString()].Points.AddXY(row["Resource"].ToString(), (int)row["Hoursworked"]);
}