Why when I run this function, "1" is appended to the variable, instead of added to it.
I guess my syntax is wrong , but couldent figure out why? :\
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName("TEST");
//get last cell with data on cloumn A
var columnA = dataSheet.getRange("A" + dataSheet.getMaxRows());
var Alast = columnA.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1);
//get last cell with data on cloumn C
var columnC = dataSheet.getRange("C" + dataSheet.getMaxRows());
var Clast = columnC.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1);
for (var counter = Clast + 1; counter <= Alast; counter = counter +1) {
Logger.log(counter);}
}
Here is the logger output:
1
11
111
Execution cancelled
Thanks
I thought that columnA.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1) and columnC.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1) return the string value. I thought that this is the reason of your issue. In your script, how about the following modification?
From:
//get last cell with data on cloumn A
var columnA = dataSheet.getRange("A" + dataSheet.getMaxRows());
var Alast = columnA.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1);
//get last cell with data on cloumn C
var columnC = dataSheet.getRange("C" + dataSheet.getMaxRows());
var Clast = columnC.getNextDataCell(SpreadsheetApp.Direction.UP).getA1Notation().slice(1);
To:
//get last cell with data on cloumn A
var columnA = dataSheet.getRange("A" + dataSheet.getMaxRows());
var Alast = columnA.getNextDataCell(SpreadsheetApp.Direction.UP).getRow(); // Modified
//get last cell with data on cloumn C
var columnC = dataSheet.getRange("C" + dataSheet.getMaxRows());
var Clast = columnC.getNextDataCell(SpreadsheetApp.Direction.UP).getRow(); // Modified
By this modification, Alast and Clast are the values of integer.
Reference:
getRow()
I have a table I created and am most interested in the "costs" column. I could have more than one row of data in which I would have multiple costs and I want to add those up and put the result in the table cell next to "Total." I saw a method called GetValue(); but not sure if that is what I am looking for or how to use it. My thinking is that migradoc has a method where you can get the value of a table cell in which I would store that in a variable. And where I create the "Total" row, I would use that variable to display the total. So how would I do that?
My code:
/define header of table
Row row = table.AddRow();
row.HeadingFormat = true;
Cell cell = row.Cells[0];
cell.AddParagraph("Customer Name");
cell.Format.Font.Bold = true;
cell = row.Cells[1];
cell.AddParagraph("Date Created");
cell.Format.Font.Bold = true;
cell = row.Cells[2];
cell.AddParagraph("Description");
cell.Format.Font.Bold = true;
cell = row.Cells[3];
cell.AddParagraph("Due Date");
cell.Format.Font.Bold = true;
cell = row.Cells[4];
cell.AddParagraph("Billable Hours");
cell.Format.Font.Bold = true;
cell = row.Cells[5];
cell.AddParagraph("Costs");
cell.Format.Font.Bold = true;
//define a row of data in the table
foreach (TicketView1 ticket in SampleTickets)
{
row = table.AddRow();
cell = row.Cells[0];
cell.AddParagraph(ticket.customer_name);
cell = row.Cells[1];
cell.AddParagraph(ticket.date_created.ToString("MM/dd/yyyy"));
cell = row.Cells[2];
cell.AddParagraph(ticket.description);
cell = row.Cells[3];
cell.AddParagraph(ticket.due_date.ToString("MM/dd/yyyy"));
cell = row.Cells[4];
cell.AddParagraph(ticket.billable_hrs.ToString());
}
cell = row.Cells[5];
cell.AddParagraph("$60.00");
//add invisible row as a space line to the table
row = table.AddRow();
row.Borders.Visible = false;
//add the subtotal row
row = table.AddRow();
row.Cells[0].Borders.Visible = false;
row.Cells[0].AddParagraph("Sub Total:");
row.Cells[0].Format.Font.Bold = true;
row.Cells[0].Format.Alignment = ParagraphAlignment.Right;
row.Cells[0].MergeRight = 4;
cell = row.Cells[5];
//add tax row
row = table.AddRow();
row.Cells[0].Borders.Visible = false;
row.Cells[0].AddParagraph("TAX:");
row.Cells[0].Format.Font.Bold = true;
row.Cells[0].Format.Alignment = ParagraphAlignment.Right;
row.Cells[0].MergeRight = 4;
//add total
row = table.AddRow();
row.Cells[0].Borders.Visible = false;
row.Cells[0].AddParagraph("TOTAL:");
row.Cells[0].Format.Font.Bold = true;
row.Cells[0].Format.Alignment = ParagraphAlignment.Right;
row.Cells[0].MergeRight = 4;
}
I have found out the answer by simple creating a count variable and then inside my forEach loop just adding that sum plus my dummy data which would be 60. Then right where I created my "totals" row, right underneath it I added into the cell that variable which contains the count data.
My forEach loop and count variable
double sum = 0;
foreach (TicketView1 ticket in SampleTickets)
{
row = table.AddRow();
cell = row.Cells[0];
cell.AddParagraph(ticket.customer_name);
cell = row.Cells[1];
cell.AddParagraph(ticket.date_created.ToString("MM/dd/yyyy"));
cell = row.Cells[2];
cell.AddParagraph(ticket.description);
cell = row.Cells[3];
cell.AddParagraph(ticket.due_date.ToString("MM/dd/yyyy"));
cell = row.Cells[4];
cell.AddParagraph(ticket.billable_hrs.ToString());
cell = row.Cells[5];
cell.AddParagraph("$60.00");
sum = sum + 60;
}
Where I added the total row and appended the data to the totals cell:
//add total
row = table.AddRow();
row.Cells[0].Borders.Visible = false;
row.Cells[0].AddParagraph("TOTAL:");
row.Cells[0].Format.Font.Bold = true;
row.Cells[0].Format.Alignment = ParagraphAlignment.Right;
row.Cells[0].MergeRight = 4;
row.Cells[5].AddParagraph(sum.ToString("$0.00"));
The issue with Table.ShowFilter, using EPPLUS library.
Created new ExcelTable in the sheet, but can't apply Table.ShowFilter = false.
The filters are still in the table.
TableStyle and StyleName work fine.
ExcelTable et = (ExcelTable)Table;
int firstRow = newRow;
int lastRow;
if (DataStructure.Data != null)
lastRow = newRow + DataStructure.Data.Count();
else
lastRow = newRow + 1;
int firstColumn = OriginalAddress.StartColumn;
int lastColumn = OriginalAddress.EndColumn;
ExcelRange rg = ws.Cells[firstRow, firstColumn, lastRow, lastColumn];
Guid guid = Guid.NewGuid();
string str_guid = guid.ToString();
string tableName = et.Name + "_" + str_guid;
ExcelTable tab = wsTarget.Tables.Add(rg, tableName);
// tab.ShowHeader = et.ShowHeader;
// tab.TableStyle = et.TableStyle;
// tab.StyleName = et.StyleName;
tab.ShowFilter = false;
Right table is desired result.
Please, help to fix!
Is this you want to mean?
tab.ShowHeader = false;
This very little example works here :
using (ExcelPackage xls = new ExcelPackage())
{
ExcelWorksheet ws2 = xls.Workbook.Worksheets.Add("f1");
OfficeOpenXml.Table.ExcelTable tab = ws2.Tables.Add(new ExcelAddressBase("a1:e5"), "table");
//tab.TableStyle = et.TableStyle;
//tab.StyleName = et.StyleName;
tab.ShowFilter = false;
FileInfo f = new FileInfo(#"d:\temp\test.xlsx");
xls.SaveAs(f);
}
Probably, the bug.
Adding ws2.DeleteColumn(1, 1) to the code, add filters to the table, even with tab.ShowFilter = false.
Workaround: put tab.ShowFilter = false right before file saving.
using (ExcelPackage xls = new ExcelPackage())
{
ExcelWorksheet ws2 = xls.Workbook.Worksheets.Add("f1");
OfficeOpenXml.Table.ExcelTable tab = ws2.Tables.Add(new ExcelAddressBase("d3:g8"), "table");
//tab.TableStyle = et.TableStyle;
//tab.StyleName = et.StyleName;
ws2.DeleteColumn(1, 1);
tab.ShowFilter = false;
FileInfo f = new FileInfo(#"test.xlsx");
xls.SaveAs(f);
}
I've been working to create a random pairing algorithm for my bass club. The idea is to pair a value in Column A (Boaters) with a value in Column B (non-boaters). If there are no more non-boaters, any remaining boaters should be paired unless only one unpaired boater remains.
I found some VBA code online, which works fine in Excel, but all of my club's stuff is in Google Sheets and I'd like to have the same pairing function in GAS.
I've tried my best to convert the VBA code to GAS, but honestly, I have some experience with VBA and I'm still a relative novice at GAS, although I'm learning.
I've pasted the two VBA functions below, followed by the GAS conversion I've been working on. The comments show the areas where I'm having trouble, particularly with the called sorting function (vSortM), although I'd welcome a second set of eyes to make sure I haven't incorrectly coded something else.
Can anyone advise if I am performing the conversion from VBA to GAS correctly?
Pairing Algorithm in VBA that I found online:
Option Explicit
Sub test()
Dim Boters(), NonBoters(), i As Long, x As Long
Boters = Range("a1", Range("a" & Rows.Count).End(xlUp)).Value
Redim Preserve Boters(1 To UBound(Boters), 1 To 2)
NonBoters = Range("b1", Range("b" & Rows.Count).End(xlUp)).Value
Redim Preserve NonBoters(1 To UBound(NonBoters), 1 To 2)
Randomize
For i = 1 To UBound(Boters)
Boters(i, 2) = Rnd
Next
For i = 1 To UBound(NonBoters)
NonBoters(i, 2) = Rnd
Next
VSortM Boters, 1, UBound(Boters), 2
VSortM NonBoters, 1, UBound(NonBoters), 2
x = Application.Min(UBound(Boters), UBound(NonBoters))
With Cells(1, 4).Resize(x, 2)
.CurrentRegion.ClearContents
.Columns(1).Value = Boters
.Columns(2).Value = NonBoters
End With
If x < UBound(Boters) Then
For i = x + 1 To UBound(Boters) Step 2
If i + 1 > UBound(Boters) Then Exit For
Cells(i, 4).Value = Boters(i, 1)
Cells(i, 5).Value = Boters(i + 1, 1)
Next
End If
End Sub
Private Sub VSortM(ary, LB, UB, ref)
Dim M As Variant, i As Long, ii As Long, iii As Long, temp
i = UB: ii = LB
M = ary(Int((LB + UB) / 2), ref)
Do While ii <= i
Do While ary(ii, ref) < M
ii = ii + 1
Loop
Do While ary(i, ref) > M
i = i - 1
Loop
If ii <= i Then
For iii = LBound(ary, 2) To UBound(ary, 2)
temp = ary(ii, iii)
ary(ii, iii) = ary(i, iii): ary(i, iii) = temp
Next
ii = ii + 1: i = i - 1
End If
Loop
If LB < i Then VSortM ary, LB, i, ref
If ii < UB Then VSortM ary, ii, UB, ref
End Sub
My attempt at converting the Pairing Algorithm in GAS
function test() {
//Get values for Column A and Column B starting at Row 5
var ssMatch = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Pairings');
var bRange = ssMatch.getRange("A5:A").getValues();
var nBRange = ssMatch.getRange("B5:B").getValues();
//Determine length with data to exclude blansk
var bLast = bRange.filter(String).length;
var nBLast = nBRange.filter(String).length;
//Get values for boaters & nBoaters without blanks
var boaters = ssMatch.getRange(5,1,bLast).getValues();
var nBoaters = ssMatch.getRange(5,2,nBLast).getValues();
// Populate boaters & nBoaters arrays using random numbers
for (var i = 0; i < bLast; i++) {
boaters[i][1] = Math.random();
Logger.log(boaters);
}
for (var j = 0; j<nBLast; j++) {
nBoaters[j][1] = Math.random();
Logger.log(nBoaters);
}
vSortM (boaters, 1, bLast, 1);
vSortM (nBoaters, 1, nBLast, 1);
//Determine whether there are more boaters or non-boaters
var x = Min(bLast, nBLast);
//Write boater & nBoater values in Columns
//NEED SOME HELP HERE: Certain this isn't correct for GAS
Cells(1,4).Resize(x, 2);
Cells.CurrentRegion.ClearContents;
Cells.Columns(1).setValues(boaters);
Cells.Columns(2).setValues(nBoaters);
//If no more nBoaters, pair remaining unpaired boaters
if (x < bLast) {
for (var i = x + 1; i<bLast; i = i + 2) {
if (i + 1 > bLast) { break;}
else {
//THINK I DID THIS RIGHT, BUT NOT SURE
ssMatch.getRange(i,4).setValue(boaters[i][0]);
ssMatch.getRange(i,5).setValue(boaters[i+1][0]);
}
}
}
}
//Having some trouble converting this from VBA to GAS
// not sure how to deal with the ary parameter and m statement
function vSortM(ary, lB, uB, ref) {
var temp = 0;
var i = uB;
var ii = lB;
var m = [parseInt((lB + uB) / 2), ref];
while (ii <= i);{
while ([ii, ref] < m); {
ii++;
while ([i, ref] > m); {
i--;
}
if (ii <= i); {
for (var iii = 0; i<=(ary, 2);) {
temp = [ii, iii];
[ii, iii] = [i, iii];
[i, iii] = temp;
}
ii++;
i--;
}
}
if (lB < i) {
vSortM(ary, lB, i, ref);
}
if (ii < uB) {
vSortM(ary, ii, uB, ref);
}
}
}
First off, I have to agree with the others in the comments. The question is too broad. In https://stackoverflow.com/help/dont-ask it says that questions should be reasonably scoped.
From what I see in your code that you ask for help.
//NEED SOME HELP HERE: Certain this isn't correct for GAS
Cells(1,4).Resize(x, 2);
Cells.CurrentRegion.ClearContents;
Cells.Columns(1).setValues(boaters);
Cells.Columns(2).setValues(nBoaters);
This section is easy to figure out by using the very well written documentation for GAS (always refer to it first before going anywhere else). In GAS you are essentially working with classes (or objects if you will). Here you will want the sheet class to resize the row and column (2 seperate functions) and then a range class (which is retrieved from the sheet class) to clear and set values.
//THINK I DID THIS RIGHT, BUT NOT SURE
ssMatch.getRange(i,4).setValue(boaters[i][0]);
ssMatch.getRange(i,5).setValue(boaters[i+1][0]);
Depends on what you want to accomplish. The syntax here is correct, you set a value for a single cell. Keep in mind that in google sheets it's best to try and batch such calls. So instead of setting a value on a cell by cell basis, you would get a range from A1 to B20 and set all values at once with a 2D array.
Finally you need to clarify what it is the second functions has to do and what it doesn't do right. Perhaps share a minimal example sheet (read here about Minimal, Complete, and Verifiable example)?
I've successfully created a pivot table using EPPlus. The pivot table resides in a seperate sheet from the raw data. I would like to copy the pivot table data to a new, third, sheet, but just the values, not the pivot defintions. Does EPPlus support this?
You can just copy the datasource range via Cache Definition:
public void PivotDataCopy()
{
const string FORMATCURRENCY = "#,###;[Red](#,###)";
var file = new FileInfo(#"c:\temp\temp.xlsx");
if (file.Exists)
file.Delete();
var pck = new ExcelPackage(file);
var workbook = pck.Workbook;
var dataworksheet = workbook.Worksheets.Add("datasheet");
//The data
dataworksheet.Cells["A20"].Value = "Col1";
dataworksheet.Cells["A21"].Value = "sdf";
dataworksheet.Cells["A22"].Value = "wer";
dataworksheet.Cells["A23"].Value = "ghgh";
dataworksheet.Cells["A24"].Value = "sdf";
dataworksheet.Cells["A25"].Value = "wer";
dataworksheet.Cells["B20"].Value = "Col2";
dataworksheet.Cells["B21"].Value = "Group A";
dataworksheet.Cells["B22"].Value = "Group B";
dataworksheet.Cells["B23"].Value = "Group A";
dataworksheet.Cells["B24"].Value = "Group C";
dataworksheet.Cells["B25"].Value = "Group A";
dataworksheet.Cells["C20"].Value = "Col3";
dataworksheet.Cells["C21"].Value = 453.5;
dataworksheet.Cells["C22"].Value = 634.5;
dataworksheet.Cells["C23"].Value = 274.5;
dataworksheet.Cells["C24"].Value = 453.5;
dataworksheet.Cells["C25"].Value = 634.5;
dataworksheet.Cells["D20"].Value = "Col4";
dataworksheet.Cells["D21"].Value = 686468;
dataworksheet.Cells["D22"].Value = 996440;
dataworksheet.Cells["D23"].Value = 185780;
dataworksheet.Cells["D24"].Value = 686468;
dataworksheet.Cells["D25"].Value = 996440;
//The pivot table
var pivotworksheet = workbook.Worksheets.Add("pivotsheet");
var pivotTable = pivotworksheet.PivotTables.Add(pivotworksheet.Cells["A1"], dataworksheet.Cells["A20:D29"], "test");
//The label row field
pivotTable.RowFields.Add(pivotTable.Fields["Col1"]);
pivotTable.DataOnRows = false;
//The data fields
var field = pivotTable.DataFields.Add(pivotTable.Fields["Col3"]);
field.Name = "Sum of Col2";
field.Function = DataFieldFunctions.Sum;
field.Format = FORMATCURRENCY;
field = pivotTable.DataFields.Add(pivotTable.Fields["Col4"]);
field.Name = "Sum of Col3";
field.Function = DataFieldFunctions.Sum;
field.Format = FORMATCURRENCY;
//Get the pivot table data source
var newworksheet = workbook.Worksheets.Add("newworksheet");
var pivotdatasourcerange = pivotTable.CacheDefinition.SourceRange;
pivotdatasourcerange.Copy(newworksheet.Cells["A1"]);
pck.Save();
}
EDIT: Doing with a VBA macro which then resaves the sheet as a non-macro XLSX:
public void PivotDataCopy()
{
const string FORMATCURRENCY = "#,###;[Red](#,###)";
var file = new FileInfo(#"c:\temp\temp.xlsm");
if (file.Exists)
file.Delete();
var pck = new ExcelPackage(file);
var workbook = pck.Workbook;
var dataworksheet = workbook.Worksheets.Add("datasheet");
//The data
dataworksheet.Cells["A20"].Value = "Col1";
dataworksheet.Cells["A21"].Value = "sdf";
dataworksheet.Cells["A22"].Value = "wer";
dataworksheet.Cells["A23"].Value = "ghgh";
dataworksheet.Cells["A24"].Value = "sdf";
dataworksheet.Cells["A25"].Value = "wer";
dataworksheet.Cells["B20"].Value = "Col2";
dataworksheet.Cells["B21"].Value = "Group A";
dataworksheet.Cells["B22"].Value = "Group B";
dataworksheet.Cells["B23"].Value = "Group A";
dataworksheet.Cells["B24"].Value = "Group C";
dataworksheet.Cells["B25"].Value = "Group A";
dataworksheet.Cells["C20"].Value = "Col3";
dataworksheet.Cells["C21"].Value = 453.5;
dataworksheet.Cells["C22"].Value = 634.5;
dataworksheet.Cells["C23"].Value = 274.5;
dataworksheet.Cells["C24"].Value = 453.5;
dataworksheet.Cells["C25"].Value = 634.5;
dataworksheet.Cells["D20"].Value = "Col4";
dataworksheet.Cells["D21"].Value = 686468;
dataworksheet.Cells["D22"].Value = 996440;
dataworksheet.Cells["D23"].Value = 185780;
dataworksheet.Cells["D24"].Value = 686468;
dataworksheet.Cells["D25"].Value = 996440;
//The pivot table
var pivotworksheet = workbook.Worksheets.Add("pivotsheet");
var pivotTable = pivotworksheet.PivotTables.Add(pivotworksheet.Cells["A1"], dataworksheet.Cells["A20:D29"], "test");
//The label row field
pivotTable.RowFields.Add(pivotTable.Fields["Col1"]);
pivotTable.DataOnRows = false;
//The data fields
var field = pivotTable.DataFields.Add(pivotTable.Fields["Col3"]);
field.Name = "Sum of Col2";
field.Function = DataFieldFunctions.Sum;
field.Format = FORMATCURRENCY;
field = pivotTable.DataFields.Add(pivotTable.Fields["Col4"]);
field.Name = "Sum of Col3";
field.Function = DataFieldFunctions.Sum;
field.Format = FORMATCURRENCY;
//add the macro to copy the table data on open
workbook.Worksheets.Add("newworksheet");
var sb = new StringBuilder();
sb.AppendLine("Private Sub Workbook_SheetCalculate(ByVal Sh As Object)");
sb.AppendLine(" Sheets(\"pivotsheet\").Cells.Copy");
sb.AppendLine(" Sheets(\"newworksheet\").Range(\"A1\").PasteSpecial Paste:=xlPasteValues");
sb.AppendLine(" Selection.Clear");
sb.AppendLine(" Application.DisplayAlerts = False");
sb.AppendLine(String.Format(" ActiveWorkbook.SaveAs \"{0}\", xlOpenXMLWorkbook", file.FullName.Replace("xlsm", "xlsx")));
sb.AppendLine(" Application.DisplayAlerts = True");
sb.AppendLine("End Sub");
pck.Workbook.CreateVBAProject();
pck.Workbook.CodeModule.Code = sb.ToString();
pck.Save();
}