Moving row data to certain tabs - office-scripts

I receive a daily Excel file that has around 15K rows and 22 columns. There are a few hundred different values in column A. I want to copy or move certain rows to predefined tabs. For example, everything with the value "Home" would be moved to Sheet2, "Work" would be moved to Sheet3 etc.
Any guides to for follow for this or ideas?

Here's my attempt at a script that gets the result I think you want. Note the use of the 'getUsedRange()' method. It helps you access ranges of various sizes. There are probably several other ways to do this in typescript/javascript or by doing the filtering with Excel APIs.
function main(workbook: ExcelScript.Workbook) {
//get the used range, assuming there is one worksheet
let worksheet = workbook.getActiveWorksheet();
let range = worksheet.getUsedRange();
let rangeValues = range.getValues();
// create an array to hold all the unique values in the first column skipping the header row
let firstCol: string[] = [];
rangeValues.forEach((curRow, index) => {
if (index > 0) {
firstCol.push(curRow[0].toString());
}
});
let uniqueFirstCol = firstCol.filter((val, ind, arr) => arr.indexOf(val) === ind);
// run thru all the unique values, filter as appropriate and add filtered values to new worksheets
uniqueFirstCol.forEach(v => {
let tempValues = rangeValues.filter((row, index) => row[0] === v);
let newSheet = workbook.addWorksheet(v);
let newRange = newSheet.getRangeByIndexes(1,0, tempValues.length, tempValues[0].length);
newRange.setValues(tempValues);
newSheet.getRangeByIndexes(0,0, 1, rangeValues[0].length).setValues([rangeValues[0]])
});
}

Related

Excel office script - Delete row (except first 2 columns) after row has been copied to a new tab

I have a script that will move a row to another table if a certain cell within that row is filled.
The script then deletes the entire row. I would like for this to continue but instead of deleting the entire row it would be great if it deleted the entire row excepting the first 2 columns.
The current script is below:
function main(workbook: ExcelScript.Workbook) {
// You can change these names to match the data in your workbook.
const TARGET_TABLE_NAME = 'TableNAdded';
const SOURCE_TABLE_NAME = 'TableN';
// Select what will be moved between tables.
const FILTER_COLUMN_INDEX = 27;
const FILTER_VALUE = 'Y';
// Get the Table objects.
let targetTable = workbook.getTable(TARGET_TABLE_NAME);
let sourceTable = workbook.getTable(SOURCE_TABLE_NAME);
// If either table is missing, report that information and stop the script.
if (!targetTable || !sourceTable) {
console.log(`Tables missing - Check to make sure both source (${TARGET_TABLE_NAME}) and target table (${SOURCE_TABLE_NAME}) are present before running the script. `);
return;
}
// Save the filter criteria currently on the source table.
const originalTableFilters = {};
// For each table column, collect the filter criteria on that column.
sourceTable.getColumns().forEach((column) => {
let originalColumnFilter = column.getFilter().getCriteria();
if (originalColumnFilter) {
originalTableFilters[column.getName()] = originalColumnFilter;
}
});
// Get all the data from the table.
const sourceRange = sourceTable.getRangeBetweenHeaderAndTotal();
const dataRows: (number | string | boolean)[][] = sourceTable.getRangeBetweenHeaderAndTotal().getValues();
// Create variables to hold the rows to be moved and their addresses.
let rowsToMoveValues: (number | string | boolean)[][] = [];
let rowAddressToRemove: string[] = [];
// Get the data values from the source table.
for (let i = 0; i < dataRows.length; i++) {
if (dataRows[i][FILTER_COLUMN_INDEX] === FILTER_VALUE) {
rowsToMoveValues.push(dataRows[i]);
// Get the intersection between table address and the entire row where we found the match. This provides the address of the range to remove.
let address = sourceRange.getIntersection(sourceRange.getCell(i, 0).getEntireRow()).getAddress();
rowAddressToRemove.push(address);
}
}
// If there are no data rows to process, end the script.
if (rowsToMoveValues.length < 1) {
console.log('No rows selected from the source table match the filter criteria.');
return;
}
console.log(`Adding ${rowsToMoveValues.length} rows to target table.`);
// Insert rows at the end of target table.
targetTable.addRows(-1, rowsToMoveValues)
// Remove the rows from the source table.
const sheet = sourceTable.getWorksheet();
// Remove all filters before removing rows.
sourceTable.getAutoFilter().clearCriteria();
// Important: Remove the rows starting at the bottom of the table.
// Otherwise, the lower rows change position before they are deleted.
console.log(`Removing ${rowAddressToRemove.length} rows from the source table.`);
rowAddressToRemove.reverse().forEach((address) => {
sheet.getRange(address).delete(ExcelScript.DeleteShiftDirection.up);
});
// Reapply the original filters.
Object.keys(originalTableFilters).forEach((columnName) => {
sourceTable.getColumnByName(columnName).getFilter().apply(originalTableFilters[columnName]);
});
}
I didn't even know where to start with this.

Deleting rows of a table: The argument is invalid, is missing or has an invalid format

When trying to delete all the rows (except the first row) of all the tables of 'MySheet'
function main(workbook: ExcelScript.Workbook) {
let sheet = workbook.getWorksheet('MySheet');
let tables = sheet.getTables();
let rowCount = 0;
tables.forEach((table) => {
rowCount = table.getRangeBetweenHeaderAndTotal().getRowCount();
table.deleteRowsAt(1, rowCount);
});
}
The script works well on the 1st table, but when looping through the second one, I get the error:
Line 8: Table deleteRowsAt: The argument is invalid, is missing or has an invalid format.
You're trying to delete the number of rows in the table but excluding the first one. So the error is because you're trying to delete one more row than is included in the table. Since you're excluding the first row, you have to subtract one from the rowCount to get it to delete correctly. You can see the updated code below. I added -1 after rowCount on the table.deleteRowsAt() line:
function main(workbook: ExcelScript.Workbook) {
let sheet = workbook.getWorksheet('MySheet');
let tables = sheet.getTables();
let rowCount = 0;
tables.forEach((table) => {
rowCount = table.getRangeBetweenHeaderAndTotal().getRowCount();
table.deleteRowsAt(1, rowCount-1);
});
}

Excel office script - copy a row to a new tab if cell is NOT empty

I have created a script that will move an entire row to another tab on the worksheet if certain text is entered into a selected cell.
I want to be able to do this if the cell is not empty rather than having certain text and I would like the row to be deleted all except the first column.
The script is below and works really well, I'm not great at coding and managed to cobble this together from some other scripts i found but i now can't manage to edit it to fit this new task.
I tried using Javascript not equals signs and other symbols and can remove rows that are empty but i can't seem to make it work.
function main(workbook: ExcelScript.Workbook) {
// You can change these names to match the data in your workbook.
const TARGET_TABLE_NAME = 'TableNAdded';
const SOURCE_TABLE_NAME = 'TableN';
// Select what will be moved between tables.
const FILTER_COLUMN_INDEX = 27;
const FILTER_VALUE = 'Y';
// Get the Table objects.
let targetTable = workbook.getTable(TARGET_TABLE_NAME);
let sourceTable = workbook.getTable(SOURCE_TABLE_NAME);
// If either table is missing, report that information and stop the script.
if (!targetTable || !sourceTable) {
console.log(`Tables missing - Check to make sure both source (${TARGET_TABLE_NAME}) and target table (${SOURCE_TABLE_NAME}) are present before running the script. `);
return;
}
// Save the filter criteria currently on the source table.
const originalTableFilters = {};
// For each table column, collect the filter criteria on that column.
sourceTable.getColumns().forEach((column) => {
let originalColumnFilter = column.getFilter().getCriteria();
if (originalColumnFilter) {
originalTableFilters[column.getName()] = originalColumnFilter;
}
});
// Get all the data from the table.
const sourceRange = sourceTable.getRangeBetweenHeaderAndTotal();
const dataRows: (number | string | boolean)[][] = sourceTable.getRangeBetweenHeaderAndTotal().getValues();
// Create variables to hold the rows to be moved and their addresses.
let rowsToMoveValues: (number | string | boolean)[][] = [];
let rowAddressToRemove: string[] = [];
// Get the data values from the source table.
for (let i = 0; i < dataRows.length; i++) {
if (dataRows[i][FILTER_COLUMN_INDEX] === FILTER_VALUE) {
rowsToMoveValues.push(dataRows[i]);
// Get the intersection between table address and the entire row where we found the match. This provides the address of the range to remove.
let address = sourceRange.getIntersection(sourceRange.getCell(i, 0).getEntireRow()).getAddress();
rowAddressToRemove.push(address);
}
}
// If there are no data rows to process, end the script.
if (rowsToMoveValues.length < 1) {
console.log('No rows selected from the source table match the filter criteria.');
return;
}
console.log(`Adding ${rowsToMoveValues.length} rows to target table.`);
// Insert rows at the end of target table.
targetTable.addRows(-1, rowsToMoveValues)
// Remove the rows from the source table.
const sheet = sourceTable.getWorksheet();
// Remove all filters before removing rows.
sourceTable.getAutoFilter().clearCriteria();
// Important: Remove the rows starting at the bottom of the table.
// Otherwise, the lower rows change position before they are deleted.
console.log(`Removing ${rowAddressToRemove.length} rows from the source table.`);
rowAddressToRemove.reverse().forEach((address) => {
sheet.getRange(address).delete(ExcelScript.DeleteShiftDirection.up);
});
// Reapply the original filters.
Object.keys(originalTableFilters).forEach((columnName) => {
sourceTable.getColumnByName(columnName).getFilter().apply(originalTableFilters[columnName]);
});
}
If I understand your question correctly, you are currently filtering the table if the value = "Y" (the value assigned to FILTER_VALUE). This part is happening here:
if (dataRows[i][FILTER_COLUMN_INDEX] === FILTER_VALUE) {
You'd like to update this line from checking if the cell value is Y to checking if the cell value is not empty. To do this, you can update this line like so:
if (dataRows[i][FILTER_COLUMN_INDEX] as string !== "") {

Office Script, update table from another, worked now failing

I used what I found Here and it worked great for a few months. My Workbook would always have the latest table update. Starting today it fails every time. Referencing Script 2 during the Flow-
Worksheet getRange: The request failed with status code of 504, error code UnknownError"
//resizes the range
let rang: ExcelScript.Range = SelectedSheet.getRange("A2").getResizedRange(valuesRowCount, valuesColumnCount)
Script 1 =
function main(workbook: ExcelScript.Workbook)
{
let selectedSheet = workbook.getActiveWorksheet();
let usedRange = selectedSheet.getUsedRange();
// Delete range B:D
selectedSheet.getRange("B:D").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range G:I
selectedSheet.getRange("G:I").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range L:L
selectedSheet.getRange("L:L").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range M:M
selectedSheet.getRange("M:M").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range N:S
selectedSheet.getRange("N:S").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range P:X
selectedSheet.getRange("P:X").delete(ExcelScript.DeleteShiftDirection.left);
// Delete range R:AW
selectedSheet.getRange("R:AW").delete(ExcelScript.DeleteShiftDirection.left);
let newTable = selectedSheet.addTable(usedRange, true);
//get table
let tbl: ExcelScript.Table = selectedSheet.getTable("Table1");
//get table's column count
let tblColumnCount: number = tbl.getColumns().length;
//set number of columns to keep
let columnsToKeep: number = 22;
//set the number of rows to remove
let rowsToRemove: number = 0;
//resize the table range
let tblRange: ExcelScript.Range =
tbl.getRangeBetweenHeaderAndTotal().getResizedRange(rowsToRemove,
columnsToKeep - tblColumnCount);
//get the table values
let tblRangeValues: string[][] = tblRange.getValues() as string[][];
//create a JSON string
let result: string = JSON.stringify(tblRangeValues);
//return JSON string
return result;
Script 2 =
function main(workbook: ExcelScript.Workbook, tableValues: string) {
let selectedSheet = workbook.getActiveWorksheet();
let SelectedSheet: ExcelScript.Worksheet = workbook.getWorksheet("Database")
let usedRange = selectedSheet.getUsedRange();
//parses the JSON string to create array
let tableValuesArray: string[][] = JSON.parse(tableValues);
//gets row count from the array
let valuesRowCount: number = tableValuesArray.length - 1
//gets column count from the array
let valuesColumnCount: number = tableValuesArray[0].length - 1
//resizes the range
let rang: ExcelScript.Range = SelectedSheet.getRange("A2").getResizedRange(valuesRowCount, valuesColumnCount)
//sets the value of the resized range to the array
rang.setValues(tableValuesArray)
// Fit the width of all the columns in the Table.
SelectedSheet.getUsedRange().getFormat().autofitColumns();
selectedSheet.getUsedRange().getFormat().setHorizontalAlignment(ExcelScript.HorizontalAlignment.left);
}
Any idea what I am doing wrong?
Are you running Script 1 as part of a Flow too? If so, you should change this line in Script 1:
let selectedSheet = workbook.getActiveWorksheet();
When you run a script as part of a Flow, you need to specify the worksheet by ID or name. So, for example, you would change the line to something like:
let selectedSheet = workbook.getWorksheet("Sheet1")
where Sheet1 is the sheet you want to add the table to.
You have two different sheet variables in Script 2 as well. One is 'selectedSheet' and the other is "SelectedSheet'. You reference both later in the script. Do you mean to have both? And again, for scripts running as part of a Flow, always specify the sheet by ID or name that you are trying to reference. Let me know if that fixes things!
Have you tried rerunning your flow? It's possible there could have been some temporary issue with PowerAutomate. So perhaps the service was down.
The only potential issue I see is this
let newTable = selectedSheet.addTable(usedRange, true);
//get table
let tbl: ExcelScript.Table = selectedSheet.getTable("Table1");
The newTable's name is not guaranteed to be Table1. So if it wasn't, your flow might not work correctly. You could get around that by setting tbl to the newTable. So you could either write code like this:
let newTable = selectedSheet.addTable(usedRange, true);
//get table
let tbl: ExcelScript.Table = newTable;
Or just update tbl to code like this:
let tbl: ExcelScript.Table = selectedSheet.addTable(usedRange, true);

How does one delete a row from filtered data

I have set a filter on my table using the following code:
let filter = taxTable.columns.getItem('Tax').filter;
filter.apply({
filterOn: Excel.FilterOn.values,
values: ['.0']
});
let filterData = taxTable.getDataBodyRange();
let visibleRange = filterData.getVisibleView().load('rowCount, rows');
await context.sync();
if (visibleRange.rowCount > 0) {
// Delete individual rows ?????
}
visibleRange is of type Excel.RangeView which does not have a row.delete() method.
So my question is how do you remove the filtered rows from the table?
To delete rows, you have to work with the Range object's row.delete(), which in your case is the flterData object.

Resources