google scripts move row to another spreadsheet reordering the columns - google-apps-script

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.

Related

Google Sheets Script to Rearrange Rows

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)
}
}

Inconsistent application of draw borders script

I have a script scheduled every evening to look at a number of different sheets and draw borders around any new data entries.
The script works yet is inconsistent. When I check it works completely for some google sheets and partially for others. There are about 10 internal sheets on each google spreadsheet and the borders are drawn in for some and not for others.
I don't understand why it is not running clean.
function addBorders() {
//Access the sheet which contains all the IDs
var idSheet = SpreadsheetApp.openById('example');
//Check for the last row
var lastRow = 0;
var currRow = 2;
//Loop through the sheet and find the last ID available
while (idSheet.getRange("A" + currRow).getValue() != "")
{
currRow = currRow + 1;
}
//Set the last row with an ID found to be the lastRow
lastRow = currRow;
idSheet.getRange("B1").setValue("Total Rows");
idSheet.getRange("B2").setValue(lastRow);
//Loop through the ID sheet to find the ID to make changes to
for( var y = 2; y < lastRow; y++)
{
var studentID = idSheet.getSheetByName("Sheet1").getRange(y, 1).getValue();
//As IDs are take from the ID sheet, open the relevant sheet and run the code below
var ss = SpreadsheetApp.openById(studentID);
var sheetsCount = ss.getNumSheets();
var sheets = ss.getSheets();
//For each sheet in the individal student spreadsheets, set the borders correctly
for (var i = 0; i < sheetsCount; i++)
{
var sheet = sheets[i];
var range = sheet.getRange(6, 3, 35);
var values = range.getValues().map(function(d){ return d[0] });
//clear previous border
var selection = sheet.getRange(6,2,35,5)
selection.setBorder(false,false,false,false,false,false);
//set border
var index = values.indexOf("");
var border = sheet.getRange(5, 2, index+1, 5);
border.setBorder(true, true, true, true, true, true);
}
}
}
This is incorrect while (idSheet.getRange("A" + currRow).getValue() != "") you must specify a sheet (i.e. tab) example: idSheet.getRange( "Sheet1!A" + currRow).getValue()!="") If this works then it's probably always using ss.getSheets()[0] the most left sheet.
This will result in not accessing the data in the last row
for( var y = 2; y < lastRow; y++)
How do you know that the index below will never be -1. If it is then index + 1 = 0 which is not a legal row value. Seems a little flaky to me.
var index = values.indexOf("");
var border = sheet.getRange(5, 2, index+1, 5);
Spreadsheet.getRange

Google Sheets: How to Move Data Between Sheets based on Text and Date

I have three sheets that I want to transfer rows of data between them based on certain conditions. From sheet1 to sheet2 based on a text string, and from sheet2 to sheet3 based on the difference between today's date the inputted date. However my attempts have been pretty fruitless thus far.
I want to move data from sheet1 to sheet2 based on whether column 'P' reads as 'Completed' or 'Thrown Out'. I also wanted to record the date it was changed to either of the above choices in column 'O'.
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var c = ss.getSheetByName("Case Logs");
var r = ss.getSheetByName("Recent Cases");
var data = c.getDataRange().getValues().map(function(x) {return x[16];});
var numColumns = c.getLastColumn();
var current = new Date();
for (var i= data.length-1; i>=0; i--) {
if(["Completed"].indexOf(data[i])>-1) {
var row = i+1;
var target = r.getRange(r.getLastRow() + 1, 1);
c.getRange('O').setValue(current);
c.getRange(row, 1, 1, numColumns).copyTo(target);
c.getRange(row, 1, 1, numColumns).clearContent();
c.getRange(row, 1, 1, numColumns).clearNote();
}
if(["Thrown Out"].indexOf(data[i])>-1) {
var row = i+1;
var target = r.getRange(r.getLastRow() + 1, 1);
c.getRange('O').setValue(current);
c.getRange(row, 1, 1, numColumns).copyTo(target);
c.getRange(row, 1, 1, numColumns).clearContent();
c.getRange(row, 1, 1, numColumns).clearNote();
}
}
}
For the second condition, I wanted to move rows from sheet2 to sheet3 based on if the date listed in column 'O' is greater than or equal to a four months difference to the current date.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var r = ss.getSheetByName("Recent Cases");
var a = ss.getSheetByName("Archived Cases");
var data = r.getDataRange().getValues().map(function(x) {return x[16];});
var numColumns = r.getLastColumn();
var TIME_FRAME = 1000 * 60 * 60 * 24 * 120;
var now = new Date();
var past = new Date(now.getTime() - TIME_FRAME);
for (var i= data.length-1; i>=0; i--) {
if (["**Column DATE**"].indexOf(logs[i])>-1 >= past){
var row = i+1;
var target2 = a.getRange(a.getLastRow() + 1, 1);
r.getRange(row, 1, 1, numColumns).copyTo(target2);
r.getRange(row, 1, 1, numColumns).clearContent();
r.getRange(row, 1, 1, numColumns).clearNote();
}
}
}
Though because I am literally slamming whatever bits of programming seem to be working, the entire thing is a mess. I do not know how to reference a specific cell within a column and confirm if it is a date, nor is the first onEdit section working anymore after I tried to have it input a date.
Here's a couple of suggestions for the first function:
Column P is 16 but arrays begin at zero and columns begin at one.
I'd replace this: var data = r.getDataRange().getValues().map(function(x) {return x[16];}); with this var data = r.getDataRange().getValues().map(function(x) {return x[15];});
I'd replace this if(["Completed"].indexOf(data[i])>-1) with this: if(data[i]=="Completed")
Personally, I would reverse the order of your loops. Your not deleting any lines and even if you were you can still keep track of how many you delete with one extra variable.
I wouldn't suggest running this on an onEdit() since your looping through all of the rows. I would use the event object to determine which row that I'm on via eventObject.range.rowStart and deal with one line at a time.
event objects
If you wish to move data from one sheet to another and delete the old rows you can do something like this:
function moveAndDeleteRows() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet85');
var tsh=ss.getSheetByName('Sheet86');
var rg=sh.getDataRange();
var n=0;
var lc=sh.getLastColumn();
var vA=rg.getValues().map(function(r){return r[15]});//this is equivalent to your get column P
for(var i=0;i<vA.length;i++) {
if(vA[i]=='Complete' || vA[i]=='Thrown Out') {
sh.getRange(i-n+1,1,1,lc).moveTo(tsh.getRange(tsh.getLastRow()+1,1));
sh.deleteRow(i-n+1);
n+=1;
}
}
}
This is the sort of function I'd use in an onEdit() it performs a similar process. I set it up by putting a data validation in column P that's either 'Completed' or 'Thrown Out'. So whenever you select one it gets moved to the other sheet and deletes the row that your on.
function moveData(e) {
if(e.range.getSheet().getName()!='Case Logs') {return;}
var tsh=e.source.getSheetByName('Recent Cases');
if(e.range.columnStart==16) {
if(e.value=='Completed' || e.value=='Thrown Out') {
e.range.getSheet().getRange(e.range.rowStart,1,1,e.range.getSheet().getLastColumn()).moveTo(tsh.getRange(tsh.getLastRow()+1,1));
e.range.getSheet().deleteRow(e.range.rowStart);
}
}
}

google script note row colorGoogle Script row color detector

I need a little assistance writing a google script function.
I have a Google Sheet where each individual row may have a different color assigned to it.
I need a script that can note the html color code found in the first cell of each row, in that cell.
So for example lets say row 1 is a green row and row 2 is a blue row, the cell A1 should say #00ff00 and A2 should say #0000ff after running the script. Below is what I have so far.
function colorDetector() {
var startRow = 1; // First row of data to process
var numRows = 3; // Number of rows to process
var currentsheet = 'Production' // What sheet you would like to process (must be within ' ')
//This section prepares the document to be read
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(currentsheet);
var dataRange = sheet.getRange(startRow, 1, numRows, 15) // Fetch values for each row in the Range (row, column, numRows, numColumns)
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
//this is the section i cant seem to get working correctly
var color = row[0].getbackground(); //should set the variable "color" to the html color found in the first cell of the current row.
sheet.getRange(startRow + i, 1).setValue(color); // notes the variable found
SpreadsheetApp.flush();
}
}
Here is what I ended up figuring out on my own. It works perfectly.
function colorDetector2() {
var startRow = 1; // First row of data to process
var currentsheet = 'sheet 4' // What sheet you would like to process (must be within ' ')
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName(currentsheet);
var numRows = 10; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 20) // Fetch values for each row in the Range (row, column, numRows, numColumns)
var colordata = dataRange.getBackgrounds();
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var color = colordata[i]
var colorconv = String(color[1]);
if (colorconv == "#00ff00") {
//do something here;
} else {
//do something else here;
}
}
}

Simple Google Spreadsheet Script to copy and paste cells gives error

I have some relatively simple code below that throws an error: : "Cannot convert NaN to (class)".
All I want to do is copy some cells from one place to another!
Can anyone please let me know what is wrong with this code?
Many thanks in advance.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
for( var row = values.length -1; row >= 0; --row )
if (values[row][5] == 'Here')
var maxRows = sheet.getMaxRows()
var startRow = (row)+1
var numRows = (maxRows)-(row)
var Range = sheet.getRange(startRow, 3, numRows, 3).getValues()
sheet.getRange(row, 3, numRows, 3).setValues(Range) // (row, column, numRows, numColumns)
}
So as the code hopefully shows, I want to copy cells in the range C:E but only rows x to getMaxRows(), where x is the row number where 'Here' is found in column F, plus 1. Then I want to paste this into the same columns C:E but one row higher than originally (into the same row as 'Here' in column F).
Any help would be very much appreciated. Thanks for looking.
EDIT: The error is on this line :
var Range = sheet.getRange(startRow, 3, numRows, 3).getValues()
In my sheet, the cells to be copied could countain blank cells and even entire blank rows. Could this be causing the issue?
Starting from your description instead of your code (why don't you use {} in loops and conditions ?) I suggest you try this
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
var maxRows = sheet.getLastRow()
var datatoCopy = []
for( var row = values.length -1; row >= 0; --row ){
if (values[row][5] == 'here'){
var whereToCopy = row+1
Logger.log(whereToCopy);
break
}
}
for(row=whereToCopy-1;row<maxRows;++row){
datatoCopy.push([values[row][2]+'**']);// store column data in an array - remove the ** that I used to see what was copied ;-)
}
Logger.log(datatoCopy)
sheet.getRange(whereToCopy, 5, datatoCopy.length, 1).setValues(datatoCopy);// overwrite data to column E
}
Following your comment, try this version ?
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet 1');
var values = sheet.getDataRange().getValues()
var maxRows = sheet.getLastRow()
var datatoCopy = []
for( var row = values.length -1; row >= 0; --row ){
if (values[row][5] == 'here'){
var whereToCopy = row
Logger.log(whereToCopy);
break
}
}
for(row=whereToCopy-1;row<maxRows-1;++row){
var rowData=[]
rowData.push(values[row+1][2]+'*C*');// I added these 'indicators' to show what happens... delete them when the result is ok ;-)
rowData.push(values[row+1][3]+'*D*');//
rowData.push(values[row+1][4]+'*E*');//
datatoCopy.push(rowData);// store column data in an array - remove the ** that I used to see what was copied ;-)
}
Logger.log(datatoCopy)
sheet.getRange(whereToCopy, 3, datatoCopy.length, datatoCopy[0].length).setValues(datatoCopy);// overwrite data to column E
}