在 excel 中使用范围对象时优化代码
Optimization of code while working with range object in excel
我最近将 Office 加载项从 vb.net 转移到 JavaScript 和 Office.js。总的来说,我观察到 JavaScript 加载项比 vb.net 快太多了。
在其中一项操作中,我无法在 JavaScript 加载项中获得速度优势。可能是我使用的代码不完善!
我有一个 master sheet,其中包含一个大 table,table 的第一列是该工作簿的所有其他 sheet 的名称,table 的第一行有单元格的地址。 table 中的其余数据是要传输到第一列中定义的 sheet 名称和第一行中定义的单元格地址的值。
在编码中,我为 table 中指示的每个值创建了一个范围对象数组,然后是 运行 上下文。 Sync() 函数每 sheets 恢复一次值。
在我的实际应用程序中 table 中的数据可以是 10K 到 50K,我可以看到这个操作花费的时间大约是。一分钟(10K)。与此相反,我只能在几秒(5 -6 秒)内创建 table(master sheet)。
是否有任何其他解决方法或建议来减少时间?
/* global Excel, console*/
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
if (arryRange.values[iRow][0] == "SheetName/CellAddress") {
addRow = iRow;
}
if (sheetName.indexOf(arryRange.values[iRow][0]) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
if (arryRange.values[addRow][iCol]) {
const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);
const range = sheet.getRange(arryRange.values[addRow][iCol]);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
首先,根据您的陈述,我假设您在 master sheet 中有一个 table。 table 的标题类似于 ["sheetName","A13","D23",...]
(“A13”和“D23”是单元格地址的示例)。在这个 table 的每一行中,包含 sheet 的名称和一些值。 sheet 的名称可能与真正的 sheet 的名称无关(不存在),并且值可能包含一些空白。并且您想根据 master sheet 的 table.
提供的信息在其他 sheet 上设置值
然后根据我的假设和你的代码我有一些建议。
- 将未更改的值移出循环。
例如,您调用了
const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);
。我们可以通过定义 var sheets = context.workbook.worksheets
和 const sheet = sheets.getItem(arryRange.values[iRow][0])
将 context.workbook.worksheets
移出循环。这可以提高性能。
还有一些重复使用的值,如 arryRange.values[iRow][0]
、arryRange.values[0][iCol]
可以移出循环。
- 似乎您使用
arryRange.values[addRow][iCol]
只是为了获取 table 标题中的地址。您可以将其替换为 arryRange.values[0][iCol]
.
下面是我重写的代码,仅供参考,可能不能完全满足你的需要
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
//var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
var cellAddress, curSheetName;
const mySheets = context.workbook.worksheets;
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
curSheetName = arryRange.values[iRow][0]
if (sheetName.indexOf(curSheetName) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
cellAddress = arryRange.values[0][iCol];
if (cellAddress) {
const sheet = mySheets.getItem(curSheetName);
const range = sheet.getRange(cellAddress);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
更多参考资料:
https://docs.microsoft.com/en-us/office/dev/add-ins/excel/performance?view=excel-js-preview
https://docs.microsoft.com/en-us/office/dev/add-ins/concepts/correlated-objects-pattern
根据您的假设,请注意 master sheet 是根据用户选择区域的实际工作簿创建的+删除 master sheet 中的空值列(我,e 空单元格每个 sheet 处的地址)。 sheet 的名称将与实际的 sheet 名称相同,除非用户不小心更改 master sheet 中的值。
参考你的,
建议 1) 我相信将未更改的值移出循环将是解决我的问题的关键。我将重新制作一个范围并获取更改的数据比较。我相信在最好的情况下,速度将得到大幅批准。 (我也会有一些最坏的情况(小于 5% 的情况),我将被要求写下 master sheet 的每个值)。
建议 2) 我打算有一个新的功能,它可能有更多的行作为地址,这就是我一直在寻找地址行的原因。
感谢您的回复。
我最近将 Office 加载项从 vb.net 转移到 JavaScript 和 Office.js。总的来说,我观察到 JavaScript 加载项比 vb.net 快太多了。
在其中一项操作中,我无法在 JavaScript 加载项中获得速度优势。可能是我使用的代码不完善!
我有一个 master sheet,其中包含一个大 table,table 的第一列是该工作簿的所有其他 sheet 的名称,table 的第一行有单元格的地址。 table 中的其余数据是要传输到第一列中定义的 sheet 名称和第一行中定义的单元格地址的值。
在编码中,我为 table 中指示的每个值创建了一个范围对象数组,然后是 运行 上下文。 Sync() 函数每 sheets 恢复一次值。
在我的实际应用程序中 table 中的数据可以是 10K 到 50K,我可以看到这个操作花费的时间大约是。一分钟(10K)。与此相反,我只能在几秒(5 -6 秒)内创建 table(master sheet)。
是否有任何其他解决方法或建议来减少时间?
/* global Excel, console*/
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
if (arryRange.values[iRow][0] == "SheetName/CellAddress") {
addRow = iRow;
}
if (sheetName.indexOf(arryRange.values[iRow][0]) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
if (arryRange.values[addRow][iCol]) {
const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);
const range = sheet.getRange(arryRange.values[addRow][iCol]);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
首先,根据您的陈述,我假设您在 master sheet 中有一个 table。 table 的标题类似于 ["sheetName","A13","D23",...]
(“A13”和“D23”是单元格地址的示例)。在这个 table 的每一行中,包含 sheet 的名称和一些值。 sheet 的名称可能与真正的 sheet 的名称无关(不存在),并且值可能包含一些空白。并且您想根据 master sheet 的 table.
然后根据我的假设和你的代码我有一些建议。
- 将未更改的值移出循环。
例如,您调用了
const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);
。我们可以通过定义var sheets = context.workbook.worksheets
和const sheet = sheets.getItem(arryRange.values[iRow][0])
将context.workbook.worksheets
移出循环。这可以提高性能。
还有一些重复使用的值,如 arryRange.values[iRow][0]
、arryRange.values[0][iCol]
可以移出循环。
- 似乎您使用
arryRange.values[addRow][iCol]
只是为了获取 table 标题中的地址。您可以将其替换为arryRange.values[0][iCol]
.
下面是我重写的代码,仅供参考,可能不能完全满足你的需要
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
//var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
var cellAddress, curSheetName;
const mySheets = context.workbook.worksheets;
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
curSheetName = arryRange.values[iRow][0]
if (sheetName.indexOf(curSheetName) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
cellAddress = arryRange.values[0][iCol];
if (cellAddress) {
const sheet = mySheets.getItem(curSheetName);
const range = sheet.getRange(cellAddress);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
更多参考资料:
https://docs.microsoft.com/en-us/office/dev/add-ins/excel/performance?view=excel-js-preview
https://docs.microsoft.com/en-us/office/dev/add-ins/concepts/correlated-objects-pattern
根据您的假设,请注意 master sheet 是根据用户选择区域的实际工作簿创建的+删除 master sheet 中的空值列(我,e 空单元格每个 sheet 处的地址)。 sheet 的名称将与实际的 sheet 名称相同,除非用户不小心更改 master sheet 中的值。 参考你的,
建议 1) 我相信将未更改的值移出循环将是解决我的问题的关键。我将重新制作一个范围并获取更改的数据比较。我相信在最好的情况下,速度将得到大幅批准。 (我也会有一些最坏的情况(小于 5% 的情况),我将被要求写下 master sheet 的每个值)。
建议 2) 我打算有一个新的功能,它可能有更多的行作为地址,这就是我一直在寻找地址行的原因。
感谢您的回复。