=>I don't speak English
=>Using google translate
I have an app Script that uses a for loop to write a value to a cell but too many rows(30000). Recording takes a lot of time. So is there any solution to shorten the time?
var dataValues = dataRange.getValues();
for(var i = 6; i < 30000; i++)
{
if(dataValues[i][0]!== '')
{
var tudongxonghang = i+1;
dataSheet.getRange('B' + tudongxonghang).setValue("hello");
Related
1. Code description: I wrote this app script that for each row, colors the cell in column A the same color as the last cell of that row with text in it. Additionally, I use an onEdit trigger, so whenever I edit a row, the script runs. This worked alright when I had about 20 rows and 20 columns (2-3 seconds).
2. Problem: I now have a sheet with about 200 rows and 20 columns and the code is extremely slow (3-4 minutes or more).
3. QUESTION: How to make it run faster, or, given what I need, should I write this task in another way?
4. Solutions I thought about but don't like:
split my sheet into several sheets (not as helpful for my use case)
add a button to run the app only when I make an edit (not as nice as an onEdit)
5. Code:
function colorFirstCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('courseX');
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var columnFirstCells = 1; // column of cell to be colored
var dataRange = sheet.getRange(1,1, lastRow, lastColumn + 1).getValues();
for(var i = 0; i < lastRow; i++)
{
for(var j = 0; j < lastColumn; j++) {
if(dataRange[i][j] != '' && dataRange[i][j+1] == '') { // cell not empty and cell to the right is empty
var backgroundColor = sheet.getRange(i + 1, j + 1).getBackground(); // get color
sheet.getRange(i + 1, columnFirstCells).setBackground(backgroundColor); // set color
of first col cell
}
}
}
}
I believe your goal is as follows.
You want to reduce the process cost of your script.
In this case, how about the following modification?
Modified script:
function colorFirstCell2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('courseX');
var range = sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn());
var backgrounds = range.getBackgrounds();
var colors = range.getDisplayValues().map((r, i) => {
for (var j = r.length - 1; j >= 0; j--) {
if (r[j] != "") {
return [backgrounds[i][j]];
}
}
return [null];
});
sheet.getRange(1, 1, colors.length).setBackgrounds(colors);
}
In this case, first, the values and background colors are retrieved from the data range. And then, an array including the background colors is created. And, the created array is put to the column "A".
References:
map()
setBackgrounds(color)
Which of the following queries are considered heavier performance wise:
//*/*/*/*/*
or
//ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*
or
/descendant-or-self::*/*/*/*
To my understanding the second would be more complex to compute?
Thanks.
About time of execution: it does not have a noticeable difference due to the fact that exist a lot of affecting factors, but anyway with almost identical tiny execution time of each requests.
On the current page each of requests performed on average in 0.07–0.09µs; 1µs = 1second * 10^-6.
let arrExecutionTime = [];
function getAverageExecutionTime(locator) {
let maxIteration = 1000;
let timeTotal = 0;
for (let i = 0; i < maxIteration; i++) {
let executionTimeStart = performance.now();
let arrNode = document.evaluate(locator, document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
arrExecutionTime.push(performance.now() - executionTimeStart);
}
for (let k = 0; k < arrExecutionTime.length; k++) {
timeTotal += arrExecutionTime[k];
}
let avgExecutionTime = timeTotal / arrExecutionTime.length;
console.log(`avgExecutionTime: ${avgExecutionTime}µs \nafter ${maxIteration} iterations \nfor locator:\"${locator}\"`);
}
getAverageExecutionTime("//*/*/*/*/*");
getAverageExecutionTime("//ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*/ancestor-or-self::*");
getAverageExecutionTime("/descendant-or-self::*/*/*/*");
I was told once that I should avoid referencing properties and such of my main game class from other classes as much as possible because it's an inefficient thing to do. Is this actually true? For a trivial example, in my Character class would
MainGame.property = something;
MainGame.property2 = something2;
MainGame.property3 = something3;
//etc.
take more time to execute than putting the same in a function
MainGame.function1();
and calling that (thereby needing to "open" that class only once rather than multiple times)? By the same logic, would
something = MainGame.property;
something2 = MainGame.property;
be slightly less efficient than
variable = MainGame.property;
something = variable;
something2 = variable;?
Think of referencing things as of operations. Every "." is an operation, every function call "()" is an operation (and a heavy one), every "+", "-" and so on.
So, indeed, certain approaches will generate more efficient code than other ones.
But. The thing is, to feel the result of inefficient approach you need to create a program performing millions of operations (or heavy tasks of the appropriate difficulty) each frame. If you are not parsing megabytes of binary data, or converting bitmaps, or such, you can not worry about performance. Although worrying about performance and efficiency is generally a good thing to do.
If you are willing to measure the efficiency of your code, you are free to measure its performance:
var aTime:int;
var a:int = 10;
var b:int = 20;
var c:int;
var i:int;
var O:Object;
aTime = getTimer();
O = new Object;
for (i = 0; i < 1000000; i++)
{
O.ab = a + b;
O.ba = a + b;
}
trace("Test 1. Elapsed", getTimer() - aTime, "ms.");
aTime = getTimer();
O = new Object;
c = a + b;
for (i = 0; i < 1000000; i++)
{
O.ab = c;
O.ba = c;
}
trace("Test 2. Elapsed", getTimer() - aTime, "ms.");
aTime = getTimer();
O = new Object;
for (i = 0; i < 1000000; i++)
{
O['ab'] = a + b;
O['ba'] = a + b;
}
trace("Test 3. Elapsed", getTimer() - aTime, "ms.");
Be prepared to run million-iteration loops so that total execution time exceeds 1 second, otherwise the precision will be too low.
rulejs subproject of handsontable is great. But I can't find anything that emulates the "paste special" of spreadsheets. So, if I write
afterOnCellMouseDown: function(r, c) {
var x = hotdata[c['row']][c['col']];
document.getElementById("valorCelCorrente").innerHTML = x;}
I get the formula, not the calculated value that the cell is displaying. Same happens with a manual copy & paste.
A fiddle with example: https://jsfiddle.net/zota/j2a04w83/3/
Any clue? Thanks a lot
Julio
I had the same issue, but solved it with the help of this question.
Firstly you can use hot.plugin.helper.cellValue('CELL REF') to get the actual value, but when using getData() you're looking at the whole set. To solve this problem I basically iterated through the array replacing any formula cells with the value (I only needed columns up until 'K'):
var aCols = ['A','B','C','D','E','F','G','H','I','J','K'];
var data = hot.getData();
for (i = 0; i < data.length; i++){
for(j = 0; j < data[i].length; j++){
if(data[i][j].toString().indexOf('=') > -1){
data[i][j] = hot.plugin.helper.cellValue (aCols[j] + (i+1));
}
}
}
Have anyone had performance issue when creating a select all / deselect all on the toolbar and the update is very slow. It took around 10 seconds to iterate through 300 records in the grid using the datasource set/get methods.
The code I used is as follows:
var data = dataSource.view();
for (var idx = 0, length = data.length; idx < length; idx++) {
if (!data[idx].IsActive) data[idx].set("IsActive", true);
}
Anyone encountered this issue? Is there a way to improve the performance?
Thanks.
Three questions:
if you are going to set IsActive to true, don't spend time checking if it is already true;
Instead of setting it assign the value directly, set is a pretty expensive operation and then do a refresh to update the UI.
Your computer is much faster than mine, in my case 300 values took much longer ;-)
Try doing:
var data = dataSource.view();
for (var idx = 0, length = data.length; idx < length; idx++) {
data[idx].Active = true;
}
grid.refresh();
Instead of calculating length of data everytime ,store length in local variable
like this
var data = dataSource.view();
var dataLength=data.length;
for (var idx = 0, length = dataLength; idx < length; idx++) {
data[idx].Active = true;
}
grid.refresh();