Google Sheets Script to Rearrange Rows - google-apps-script

Trying to create a Google Sheet that allows selection of row priority with dropdown selection option.
Here's a basic algorithm that I am trying to build out:
1 User changes the priority of the row using the dropdown (Note: That part works fine)
2 The edited row will move up to the position based on the new priority (Note: That part works fine)
3 All other rows will change their numbers based on the edited cell (i.e. Say, the edited cell was originally 13 and changed to 4. I want the old 4 to become 5 and everything from 5 to 12 to increase 1)
The issue I'm having is when the cell is changed to number 4, it goes under the old number 4 - hence a for loop doesn't work.
Anyone done anything similar or have suggestions?
function onEdit(event) {
var activeSheet = SpreadsheetApp.getActiveSheet();
var editedCell = activeSheet.getActiveCell();
var columnToSortBy = 1;
var tableRange = "A2:M22";
if(editedCell.getColumn() == columnToSortBy){
var priority = editedCell.getValue();
var range = activeSheet.getRange(tableRange);
var time = new Date();
time = Utilities.formatDate(time, "GMT", "HH:mm:ss");
var nextCell = editedCell.offset(0, 13);
nextCell.setValue(time);
range.sort({ column: columnToSortBy, ascending: true});
}
}
function rearrangeRows() {
var i;
var ss = SpreadsheetApp.getActiveSheet();
var startRow = 1;
var endRow = ss.getLastRow();
var getRange = ss.getDataRange();
var getRow = getRange.getRow();
var testPriority = 3;
var cell = ss.getRange("A4");
var cellValue = cell.getValue();
if (cellValue > number) {
Logger.log(cellValue);
}
Logger.log(number);
// for(i; i > )
}

I think you could put code like what I've written below in the onEdit function, before you sort the spreadsheet. This should increase the value of all the cells with a priority higher than the priority you just set, ignoring the cell you just changed. You may have to adapt it slightly to work also when you decrease the priority of a row, as you'll want to decrease the priority of all the rows in between the row's original priority and its new priority.
var endRow = activeSheet.getLastRow();
for (var i = 1; i <= endRow; i ++) {
var currentCell = activeSheet.getRange("A" + i);
if (currentCell.getValue() > priority && i != editedCell.getRow()) {
currentCell.setValue(currentCell.getValue() + 1)
}
}

Related

Anyone can help me to speed up this Google Sheet script?

I am using the following script to refresh my Google Finance watchlist. I am not a programmer so that's why I copied this from somewhere and use it as long as it does the trick. It certainly works for me but it's kinda slow, and I hope someone can help me to optimize this script to make it better.
My understanding is, this script basically scan the whole table (7 columns and over 60 rows) to find out the no. of rows and column. It first empty the cells then fill the cell with original formula (which is GoogleFinance function for stock quote, something like that) to force the update for the whole table. I actually just need to trigger update for a specific column but I don't know how to do it (yep, not a programmer..). I tried to change the row = x and col = x to another values and it would help to reduce the scope of the update size of the table, and it help me abit only.
In order to make it update more frequently, I actually copy and paste those two for loops set 5 more times and use Utilities.sleep(10000); in between to make the script run 6 rounds a minute. So I can bypass the minimum 1 minute interval in Google Sheet setting. But yes, I know it's cause the script run even slower...
I hope someone can help me to speed this script up, really appreciate anyone can help! Thanks you!!
function forceRefreshSheetFormulas(sheetName) {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = activeSpreadsheet.getSheetByName('*Table Name*');
var range = sheet.getDataRange();
var numCols = range.getNumColumns();
var numRows = range.getNumRows();
var rowOffset = range.getRow();
var colOffset = range.getColumn();
// Change formulas then change them back to refresh it
var originalFormulas = range.getFormulas();
//Loop through each column and each row in the sheet
//`row` and `col` are relative to the range, not the sheet
for (row = 0; row < numRows ; row++){
for(col = 0; col < numCols; col++){
if (originalFormulas[row][col] != "")
range.getCell(row+rowOffset, col+colOffset).setFormula("");
}
};
};
SpreadsheetApp.flush();
for (row = 0; row < numRows ; row++){
for(col = 0; col < numCols; col++){
if (originalFormulas[row][col] != "") {
range.getCell(row+rowOffset, col+colOffset).setFormula(originalFormulas[row][col]);
}
};
};
SpreadsheetApp.flush();
Try
pls enable Sheets API at Advanced Google services.
function forceRefreshSheetFormulas(sheetName) {
var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = activeSpreadsheet.getSheetByName('*Table Name*');
var range = sheet.getDataRange();
var numCols = range.getNumColumns();
var numRows = range.getNumRows();
var rowOffset = range.getRow();
var colOffset = range.getColumn();
// Change formulas then change them back to refresh it
var originalFormulas = range.getFormulas();
//Loop through each column and each row in the sheet
//`row` and `col` are relative to the range, not the sheet
var ranges = []
var blank = []
var original = []
for (row = 0; row < numRows; row++) {
for (col = 0; col < numCols; col++) {
if (originalFormulas[row][col] != ""){
ranges.push( `'*Table Name*'!${columnToLetter(col + colOffset)}${row + rowOffset}` )
blank.push('')
original.push(`${originalFormulas[row][col]}`)
};
}
};
updateGoogleSheet(ranges,blank)
SpreadsheetApp.flush();
updateGoogleSheet(ranges,original)
};
function updateGoogleSheet(ranges,values) {
var spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId()
var data = ranges.map((e, i) => ({ range: e, values: [[values[i]]] }));
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, spreadsheetId);
}
function columnToLetter(column) {
var temp, letter = '';
while (column > 0) {
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
reference
batchupdate

Get Selected Row's Specific Col Value via script (Google Sheets)

I've been trying to get this one to work without success so far.
I need to get the TaskNumber on the first column of the row I'm on and bring it to the destination sheet, so that I can update it there.
I have the following, which I'm tweaking to achieve my goal, but I guess I've bumped into my limitation walls:
function jump() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var TaskNumberSheet = ss.getSheetByName('To-Do List');
var TaskNoRow = TaskNumberSheet.getActiveCell();
var TaskNoCol = TaskNoRow.getColumn() == 1
var TaskNo = TaskNoCol.getValue;
var sheet = ss.getSheetByName('Updates');
//var Tasks = ss.getSheetByName("To Do List").getActiveRange(Tasks.getColum(1)).getValue();
var values = sheet.getRange("B:B").getValues();
var maxIndex = values.reduce(function(maxIndex, row, index) {
return row[0] === "" ? maxIndex : index;
}, 0);
sheet.setActiveRange(sheet.getRange(maxIndex + 2,2)).setValue(TaskNo);
}
Any help is appreciate.
Cheers,
A
If I understood you correctly, you want to:
Get the value in column A from the currently active row (in sheet To-Do List).
Find the first empty cell in column B (in sheet Updates) (start looking at row #8).
Copy the value that was retrieved in step 1 to the cell retrieved in step 2.
Set the cell retrieved in step 2 as the active cell.
If that's the case, you can do the following:
function jump() {
var ss = SpreadsheetApp.getActive();
// Step 1:
var TaskNumberSheet = ss.getSheetByName('To-Do List');
var TaskNoRow = TaskNumberSheet.getActiveCell().getRow();
var TaskNoCol = 1;
var TaskNo = TaskNumberSheet.getRange(TaskNoRow, TaskNoCol).getValue();
// Step 2:
var sheet = ss.getSheetByName('Updates');
var firstRow = 8;
var column = 2;
var numRows = sheet.getLastRow() - firstRow + 1;
var values = sheet.getRange(firstRow, column, numRows).getValues().map(function(value) {
return value[0]
});
var i = 0;
for (i; i < values.length; i++) {
if (values[i] === "") break;
}
var targetRange = sheet.getRange(i + firstRow, column);
targetRange.setValue(TaskNo); // Step 3
sheet.setActiveRange(targetRange); // Step 4
}
function jump() {
var TargetRow=?;//Fillin target row
var TargetCol=?;//Fillin target column
var ss=SpreadsheetApp.getActive();
var TaskNumberSheet=ss.getSheetByName('To-Do List');
var TaskNoRow=TaskNumberSheet.getActiveCell().getRow();//Getting row from active cell
var TaskNoCol=1
var TaskNo=TaskNumberSheet.getRange(TaskNoRow,TaskNoCol).getValue();
ss.getSheetByName('Updates').getRange(targetRow,targetCol).setValue(TaskNo);
}

SORT script works for owner but not editors

I've written script to sort a google sheet by column A, and then take you to the cell next to the one you just edited, and I have it working perfectly for me the owner, but for editors, nothing seems to work starting from the "sort" function.
I assume this means some part of it is an installable trigger? but as far as I can tell I've made it all with simple triggers.
function onEdit(e) {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var cell = sheet.getActiveCell();
var NEXTcell = sheet.getActiveRange().offset(0,1);
var range = e.range;
var columnOfCellEdited = range.getColumn();
var sheet = spreadsheet.getActiveSheet();
// When Column A edited
if (columnOfCellEdited === 1) {// Column 1 is Column A
//Set marker
NEXTcell.setValue('sorting');
// Sort whole sheet excluding first 2 rows by Column A.
sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).activate();
spreadsheet.getActiveRange().offset(2, 0, spreadsheet.getActiveRange().getNumRows() - 2).sort({column: 1, ascending: true});
// Find original cell after sorting
var rowoffset = 3;
var rng = sheet.getRange(rowoffset, 2, 600);
var data = rng.getValues();
var search = "sorting";
for (var i=0; i < data.length; i++) {
if (data[i][0] == search) {
data[i][0] = "";
rng.setValues(data);
var foundcell = sheet.getRange((i+rowoffset), 2);
foundcell.activate();
break;
}
}
}
}
So Stackdriver logs for-the-win.
Turns out protections I hadn't considered are the culprit.

google scripts move row to another spreadsheet reordering the columns

I have a 4 sheet Google Sheets workbook. The 'Main' sheet has the data dumped into the first 11 columns automatically. The 12th column is set up as the manual move trigger, to move selected column data, on a per row basis, to the target sheet (one of the other 3 sheets). I'm able to move only the columns that I want to the selected target sheet, for example columns 1, 4, 9 and 10. But those are the columns that get populated in the target sheet when the data moves. I want the data to move into the first 4 columns of the target sheet instead. I've searched a number of forums and can't find if this is even possible to do. This is my current script:
function onEdit() {
// base definitions
var ss = SpreadsheetApp.getActiveSpreadsheet();
var editedSheet = ss.getActiveSheet();
var editedCell = editedSheet.getActiveCell();
var wordInCell = editedCell.getValue().toString();
var moveSheet = "Main";
var moveColumn = 12;
var moveWords = ["move5x", "move6x", "moveas"];
var targetSheets = ["5x", "6x", "Assigned"];
// if the edit was mot in the moveSheet jump out
var sheetIndex = moveSheet.indexOf(editedSheet.getName());
if (sheetIndex == -1) return;
// if the edit was mot in the move column jump out
if (editedCell.getColumn() != moveColumn) return;
// if the value in editedCell isn't one of the move words, jump out
var moveWordsIndex = moveWords.indexOf(wordInCell);
if (moveWordsIndex == -1) return;
// if the value in editedCell is one of the move words, set up columnsToMove
var columnsToMove;
if (moveWordsIndex == 0 || moveWordsIndex == 1) {columnsToMove = [[1, 4], [8, 8]]};
if (moveWordsIndex == 2) {columnsToMove = [[1, 1], [4, 4], [9, 10]]};
// copy data from editedSheet's sourceRow to targetSheet's targetRow
var sourceRow = editedCell.getRow();
var targetSheet = ss.getSheetByName(targetSheets[moveWordsIndex]);
var targetRow = targetSheet.getLastRow();
targetSheet.insertRowAfter(targetRow++);
for (var i = 0; i < columnsToMove.length; i++) {
var startColumn = columnsToMove[i][0];
var numColumns = columnsToMove[i][1] - startColumn + 1;
var numCopiedColumns = columnsToMove.length;
var rangeToCopyFrom = editedSheet.getRange(sourceRow, startColumn, 1, numColumns);
var rangeToCopyTo = targetSheet.getRange(targetRow, startColumn, 1, numColumns);
rangeToCopyFrom.copyTo(rangeToCopyTo);
}
}
I have tried manipulating rangeToCopyTo every way I could think of, but the results are never what I'm looking for.
Does anyone know if reordering selected columns is even possible? If it is, am I at least on the right track to doing it?
Thanks
Why don't you set your target range's start column to 1?
var rangeToCopyTo = targetSheet.getRange(targetRow, 1, 1, numColumns);
It should do the trick if I got you right.
You could also go with .getValues() and .setValues() stuff that works in a pretty similar way.
And there's also a way to you use separate .setValue() function:
for (var i = 0; i < columnsToMove.length; i++) {
var startColumn = columnsToMove[i][0];
var numColumns = columnsToMove[i][1] - startColumn + 1;
var numCopiedColumns = columnsToMove.length;
var rangeToCopyFrom = editedSheet.getRange(sourceRow, startColumn, 1, numColumns).getValues();
for (var i = 0; i < numCopiedColumns; i++) {
//here you insert your values into first 'numCopiedColumns' columns in the targetRow
targetSheet.getRange(targetRow, i + 1).setValue(rangeToCopyFrom[i]);
}
}
It will probably be more time-consuming as you are performing .setValue() operation for each separate value instead of pasting the data as a whole but this can give you more freedom in choosing the target columns.

How to add a button per row in google spreadsheet?

I have a simple spreadsheet with multiple rows I want to add button to each of it automatically.
On click this button will remove the entire row and move it to another spreadsheet.
I've looked online and couldn't find a way to add buttons to spreadsheets. Some people suggest to insert drawings and assign macros, but looks like the latest version lacks this feature.
I've also read about google app sript. I was able to add buttons to a menu, but not directly to each row of a spreadsheet.
Do you have any advice or suggestion on how to better achieve this?
I suggest the you create a column(say 10) at the end of each row with a List Data Validation of "Move Row" and create an onEdit function like so
function onEdit(eventInfo) {
if (eventInfo.range.getColumn() == 10 && eventInfo.value == "Move Row")
{
var ss = eventInfo.source;
var row = eventInfo.range.getRow();
var range = ss.getRange(row, 1, 10) // this is your range, do with it what you will
}
}
So this script is not entirely my own work, but the results of something I was working on with help.
It will look in a source sheet, look at a column you specify (by number), and look for a value in this sheet.
If matched, it will move that row to another sheet, within the spreadshet, of your choosing.
function moveRowInternalSheet()
{
var sourceSheet = "";
var columnNumberToWatch = ;
var valueToFind = "";
var destinationSheet = "";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheet);
var values = sheet.getDataRange().getValues();
var counter = 1; //starts looking in second row, due to header
var archive = [];
while (counter < values.length)
{
if (values[counter][columnNumberToWatch - 1] == valueToFind)
{
archive.push(values.splice(counter, 1)[0]);
}
else
{
// If the value to find is not found. Then increment the counter by 1
counter++;
}
}
var archiveLength = archive.length;
if (!archiveLength) return;
var targetSheet = ss.getSheetByName(destinationSheet);
var lastRow = targetSheet.getLastRow();
var requiredRows = lastRow + archiveLength - targetSheet.getMaxRows();
if (requiredRows > 0) targetSheet.insertRowsAfter(lastRow, requiredRows);
targetSheet.getRange(lastRow + 1, 1, archiveLength, archive[0].length).setValues(archive);
sheet.clearContents();
sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
}