I'm trying to add a slicer to connect to multiple pivot tables in Excel using office scripts. It seems like the office scripts can only connect 1 slicer to 1 pivot table. The recording action does not seem to be able to record the connectivity action in the pivot table slicer settings.
let newSlicer = workbook.addSlicer(newPivotTable, newPivotTable.getHierarchy("Overdue").getFields()[0], selectedSheet);
The above script does not seem to be able to pass in more than one pivot table. Anyone got a solution to this? Much appreciation.
I don't think linking multiple PivotTables to a slicer is currently supported. But there may be a workaround. You can run the Office Scripts code below. You will have to update the variables with the names for your own PivotTable, field for the slicer, etc.:
function main(workbook:ExcelScript.Workbook){
let sh: ExcelScript.Worksheet = workbook.getActiveWorksheet();
let slicer1: ExcelScript.Slicer = getOrAddSlicer("PivotTable1","Col1",workbook);
let slicer2: ExcelScript.Slicer = getOrAddSlicer("PivotTable1", "Col2", workbook);
}
function getOrAddSlicer(ptName:string,ptRowHierarchyName: string, workbook:ExcelScript.Workbook): ExcelScript.Slicer {
let sh: ExcelScript.Worksheet = workbook.getActiveWorksheet();
let pt: ExcelScript.PivotTable = sh.getPivotTable(ptName);
let pf: ExcelScript.PivotField = pt.getRowHierarchy(ptRowHierarchyName).getPivotField(ptRowHierarchyName);
let slicer: ExcelScript.Slicer = workbook.getSlicer(ptRowHierarchyName);
if (slicer === undefined) {
slicer = workbook.addSlicer(pt, pf, sh);
}
return slicer;
}
The getOrAddSlicer function will add a slicer to the active worksheet. Or select a slicer on the active sheet that's linked to a specific field if it's previously been added. After you've added all the slicers, you can copy and paste the PivotTable the slicers are linked to. After you've copied and pasted the PT, both PivotTables should also be linked to all of the slicers.
Related
I have two datasources as following:
Orders.csv
DepartmentID;Status;OrderID;Amount
1001;C;3;756,00
1002;B;4;45,00
1002;C;5;51,00
1003;A;6;321,00
1004;B;7;51,00
1004;C;8;21,00
Departments.csv
DepartmentID;DepartmentName;CostCenter
1001;Sales;10
1002;Accounting;20
1003;HR;30
1004;HQ;40
I have:
created a PowerPivot model with join on DepartmentID
defined a slicer Slicer_DepartmentName from the Departments table
defined a measure TotalAmount as SUM([Orders].Amount)
Now using Cube functions I want to get
CostCenter for DeparmentName selected by the slicer
Distinct statuses in Order table filtered by the slicer
The only solution I came up with are following
Cost Center:
CUBERANKEDMEMBER("ThisWorkbookDataModel";"EXISTS([Departments].[CostCenter].children,[Departments].[DepartmentName].[All].["&CUBERANKEDMEMBER("ThisWorkbookDataModel";Slicer_DepartmentName;1)&"])";1)
Statuses:
CUBERANKEDMEMBER("ThisWorkbookDataModel";"NONEMPTY([Orders].[Status].[ALL].children,([Measures].[TotalAmount],[Departments].[DepartmentName].[All].["&CUBERANKEDMEMBER("ThisWorkbookDataModel";Slicer_DepartmentName;1)&"]))";1)
changing the last parameter for the 1..3
I find my solution quite ugly and the "Statuses" part works only if there is only one DepartmentName selected in the slicer. Is there any better solution?
A looking for a way to get specific columns by name from several tables. My data comes in several sheets with different number of columns upto 38 columns so i cannot use getColumnById. I only need 7 columns from this.
First am converting all sheet ranges to tables, then am getting all tables.
What I want is to get specific columns by names and merge all into one table on a new sheet.
I followed example from Docs but am stuck at getting column name for each Table.
I know my header Values, shown in example below.
function main(workbook: ExcelScript.Workbook) {
let sheets = workbook.getWorksheets();
for (let sheet of sheets) {
sheet.getTables()[0].convertToRange();
sheet.addTable(sheet.getRange('A1').getUsedRange().getAddress(),true)
}
workbook.getWorksheet('Combined')?.delete();
const newSheet = workbook.addWorksheet('Combined');
const tables = workbook.getTables();
const headerValues = [['Column1', 'Column6', 'Column8', 'Column9','Column11', 'Column16', 'Column18', 'Column19']];
const targetRange = newSheet.getRange('A1').getResizedRange(headerValues.length - 1, headerValues[0].length - 1);
targetRange.setValues(headerValues);
const combinedTable = newSheet.addTable(targetRange.getAddress(), true);
for (let table of tables) {
let dataValues = table.getColumnByName( // this where am stuck //).getRangeBetweenHeaderAndTotal().getTexts();
let rowCount = table.getRowCount();
// If the table is not empty, add its rows to the combined table.
if (rowCount > 0) {
combinedTable.addRows(-1, dataValues);
}
}
}
Thanks for your help.
George
A few things:
In most circumstances for this scenario, I'd recommend iterating
through a specific set of table objects. Unfortunately, that's
difficult to do here. Every time you unlink and recreate a new table,
Excel may give your table a new name. That makes it difficult to
work with the table. You can get around this in your code by
capturing the table name before you unlink it, unlinking the table,
recreating the table, and setting the table name to the original one
captured. If you go that route then you could reliably work with the
table names
Because table names in this scenario can be a bit tricky, I'm going
to use the sheet names so that I can work with the sheets that contain
the underlying tables. This will allow us to use and get data from the
tables regardless of what they're named in the sheets.
Please see my code below:
function main(workbook: ExcelScript.Workbook) {
//JSON object called SheetAndColumnNames. On the left hand side is the sheet name.
//On the right hand side is an array with the column names for the table in the sheet.
//NOTE: replace the sheet and column names with your own values
let columnNames : string[] = ["ColA","ColB","ColC"]
const sheetAndColumnNames = {
"Sheet1": columnNames,
"Sheet2": columnNames
}
//JSON object called columnNamesAndCellValues. On the left hand side is the column name.
//On the right hand side is an array that will hold the values for the column in the table.
//NOTE: replace these column names with your own values
const columnNamesAndCellValues = {
"ColA": [],
"ColB": [],
"ColC": []
}
//Iterate through the sheetAndColumnNames object
for (let sheetName in sheetAndColumnNames) {
//Use sheet name from JSON object to get sheet
let sheet: ExcelScript.Worksheet = workbook.getWorksheet(sheetName)
//get table from the previously assigned sheet
let table: ExcelScript.Table = sheet.getTables()[0]
//get array of column names to be iterated on the sheet
let tableColumnNames: string[] = sheetAndColumnNames[sheetName]
//Iterate the array of table column names
tableColumnNames.forEach(columnName=> {
//get the dataBodyRange of the tableColumn
let tableColumn : ExcelScript.Range = table.getColumn(columnName).getRangeBetweenHeaderAndTotal()
//iterate through all of the values in the table column and add them to the columnNamesAndCellValues array for that column name
tableColumn.getValues().forEach(value=>{
columnNamesAndCellValues[columnName].push(value)
})
})
}
//Delete previous worksheet named Combined
workbook.getWorksheet("Combined")?.delete()
//Add new worksheet named Combined and assign to combinedSheet variable
let combinedSheet : ExcelScript.Worksheet = workbook.addWorksheet("Combined")
//Activate the combined sheet
combinedSheet.activate()
//get the header range for the table
let headerRange : ExcelScript.Range = combinedSheet.getRangeByIndexes(0,0,1,columnNames.length)
//set the header range to the column headers
headerRange.setValues([columnNames])
//iterate through the arrays returned by the columnNamesAndCellValues object to write to the Combined sheet
columnNames.forEach((column,index)=>{
combinedSheet.getRangeByIndexes(1, index, columnNamesAndCellValues[column].length, 1).setValues(columnNamesAndCellValues[column])
})
//Get the address for the current region of the data written from the tableColumnData array to the sheet
let combinedTableAddress : string = combinedSheet.getRange("A1").getSurroundingRegion().getAddress()
//Add the table to the sheet using the address and setting the hasHeaders boolean value to true
combinedSheet.addTable(combinedTableAddress,true)
}
I have a table on a spreadsheet and I want to delete all the existing data. I use the code below which works except when the table is already empty.
// Get the row count
let rowCount = table.getRangeBetweenHeaderAndTotal().getRowCount();
// Delete all the rows
table.deleteRowsAt(0,rowCount);
The problem is that rowCount will return 1 even if the table is empty. When deleteRowsAt tries to delete a row in an empty table it returns an error.
In VBA we can use table.ListRows.Count and this will return 0 if the table is empty.
Example: I have 3 rows in the table
If I select all the rows and delete them from the Table I get this:
This table now has no rows but I have no way to get this result. As I said, in VBA we would use table.ListRows.Count and this would return 0 but I cannot seem to find the equivalent for Office Scripts.
UPDATE:
We now have getRowCount API on the table that you can use to solve this scenario. It'll return the actual rows (not counting the header or expansion row).
// Assuming there's a table in the workbook named Table1
let rowCount = workbook.getTable('Table1').getRowCount(); // Table1
// Assuming there's a table in the workbook
let rowCount = workbook.getTables()[0].getRowCount(); // First table
====
OLD ANSWER
I think we are lacking an API that will provide row count. We'll add that to the backlog.
A workaround is this -
function main(workbook: ExcelScript.Workbook) {
let table = workbook.getTable("Table26");
let rowCount = table.getRangeBetweenHeaderAndTotal().getRowCount();
try {
table.deleteRowsAt(0, rowCount);
} catch (e) {
if (rowCount === 1 && e.code === 'InvalidArgument') {
console.log("This error means there's no row to delete.")
}
}
}
If row count is 1 and the error code is a specific one that's returned when we delete the 'insert-row', then we can ignore the error.
Again, a better approach would be to use an API on the table object to get the row count and delete rows only when row count >= 1. We'll investigate further to improve the experience.
I ran some quick tests deleting 5,000 rows (5 columns, different data types) using deleteRowsAt() and it consistently took 20 to 30 seconds. But ~10,000 rows or more and I got a timeout every time, the script never finished. Calling the script from Flow also gave me gateway timeouts with multiple reattempts before failing.
table.deleteRowsAt(0, rowCount);
But using getRangeBetweenHeaderAndTotal() to reference a range for deletion worked well, 200,000 rows of the same test data in about 2 seconds. The documentation mentions ranges are faster, they certainly are in my tests.
table.getRangeBetweenHeaderAndTotal().delete(0);
I had 10 power pivot tables which report filters needs to be controlled by a cell reference using excel VBA in Excel-2010
The same thing works for normal pivot table .
NewCat = Worksheets(6).Range("G12").Value
Set pt1 = Worksheets(5).PivotTables("PivotTable3")
Set Field1 = pt1.PivotFields("[Lead_f].[Lead_Generation_Month].
[Lead_Generation_Month]")
With pt1
Field1.CurrentPage = NewCat
pt1.RefreshTable
End With
Assuming that the main idea is having all 10 pivots in sync, the non VBA way to this would be to add a slicer to one of the pivot tables for filtering. Then go to each of the other pivots and set the filter connection to your slicer.
Tried writing large dataset to excel, and able to do that well with SXSSFWorkbook instead of xssfworkbook.
Now I am trying to create a pivot table with the already-written large dataset as base data. Unfortunately, SXSSFSheet does not have createPivotTable: only XSSFSheet has that facility.
Is there anyway I can use SXSSFSheet to create pivot tables?
In my case, I use SXSSFWorkbook to create a large .xlsx with pivot in few sheet.
So I create these sheet with below code. And make sure your source table is also XSSFSheet or it will cause some error. (Sheet not related to the pivot could be Sheet/SXSSFSheet is fine)
Hope this solution could help you to resolve your question.
XSSFSheet sheet = workbook.getXSSFWorkbook().createSheet("Pivot sheet");
AreaReference ar = new AreaReference("A1:" + "AI" + (source.getLastRowNum() + 1));
CellReference cr = new CellReference("A1");
XSSFPivotTable pivotTable = sheet.createPivotTable(ar, cr, source);