Inconsistent application of draw borders script - google-apps-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

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

I need to split a Google Sheet into multiple tabs (sheets) based on column value

I have searched many possible answers but cannot seem to find one that works. I have a Google Sheet with about 1600 rows that I need to split into about 70 different tabs (with about 20-30 rows in each one) based on the value in the column titled “room”. I have been sorting and then cutting and pasting but for 70+ tabs this is very tedious.
I can use the Query function but I still need to create a new tab, paste the function and update the parameter for that particular tab.
This script seemed pretty close:
ss = SpreadsheetApp.getActiveSpreadsheet();
itemName = 0;
itemDescription = 1;
image = 2;
purchasedBy = 3;
cost = 4;
room = 5;
isSharing = 6;
masterSheetName = "Master";
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Update Purchases')
.addItem('Add All Rows To Sheets', 'addAllRowsToSheets')
.addItem('Add Current Row To Sheet', 'addRowToNewSheet')
.addToUi();
}
function addRowToNewSheet() {
var s = ss.getActiveSheet();
var cell = s.getActiveCell();
var rowId = cell.getRow();
var range = s.getRange(rowId, 1, 1, s.getLastColumn());
var values = range.getValues()[0];
var roomName = values[room];
appendDataToSheet(s, rowId, values, roomName);
}
function addAllRowsToSheets(){
var s = ss.getActiveSheet();
var dataValues = s.getRange(2, 1, s.getLastRow()-1, s.getLastColumn()).getValues();
for(var i = 0; i < dataValues.length; i++){
var values = dataValues[i];
var rowId = 2 + i;
var roomName = values[room];
try{
appendDataToSheet(s, rowId, values, roomName);
}catch(err){};
}
}
function appendDataToSheet(s, rowId, data, roomName){
if(s.getName() != masterSheetName){
throw new Error("Can only add rows from 'Master' sheet - make sure sheet name is 'Master'");
}
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
var roomSheet;
if(sheetNames.indexOf(roomName) > -1){
roomSheet = ss.getSheetByName(roomName);
var rowIdValues = roomSheet.getRange(2, 1, roomSheet.getLastRow()-1, 1).getValues();
for(var i = 0; i < rowIdValues.length; i++){
if(rowIdValues[i] == rowId){
throw new Error( data[itemName] + " from row " + rowId + " already exists in sheet " + roomName + ".");
return;
}
}
}else{
roomSheet = ss.insertSheet(roomName);
var numCols = s.getLastColumn();
roomSheet.getRange(1, 1).setValue("Row Id");
s.getRange(1, 1, 1, numCols).copyValuesToRange(roomSheet, 2, numCols+1, 1, 1);
}
var rowIdArray = [rowId];
var updatedArray = rowIdArray.concat(data);
roomSheet.appendRow(updatedArray);
}
But I always get an unexpected token error on line 51 or 52:
var sheetNames = [sheet.getName() for each(sheet in ss.getSheets())];
(And obviously the column names, etc. are not necessarily correct for my data, I tried changing them to match what I needed. Not sure if that was part of the issue.)
Here is a sample of my data: https://docs.google.com/spreadsheets/d/1kpD88_wEA5YFh5DMMkubsTnFHeNxRQL-njd9Mv-C_lc/edit?usp=sharing
This should return two separate tabs/sheets based on room .
I am obviously not a programmer and do not know Visual Basic or Java or anything. I just know how to google and copy things....amazingly I often get it to work.
Let me know what else you need if you can help.
Try the below code:
'splitSheetIntoTabs' will split your master sheet in to separate sheets of 30 rows each. It will copy only the content not the background colors etc.
'deleteTabsOtherThanMaster' will revert the change done by 'splitSheetIntoTabs'. This function will help to revert the changes done by splitSheetIntoTabs.
function splitSheetIntoTabs() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var header = rows[0];
var contents = rows.slice(1);
var totalRowsPerSheet = 30; // This value will change no of rows per sheet
//below we are chunking the toltal row we have into 30 rows each
var contentRowsPerSheet = contents.map( function(e,i){
return i%totalRowsPerSheet===0 ? contents.slice(i,i+totalRowsPerSheet) : null;
}).filter(function(e){ return e; });
contentRowsPerSheet.forEach(function(e){
//crate new sheet here
var currSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
//append the header
currSheet.appendRow(header);
//populate the rows
e.forEach(function(val){
currSheet.appendRow(val);
});
});
}
// use this function revert the sheets create by splitSheetIntoTabs()
function deleteTabsOtherThanMaster() {
var sheetNotToDelete ='Master';
var ss = SpreadsheetApp.getActive();
ss.getSheets().forEach(function(sheet){
if(sheet.getSheetName()!== sheetNotToDelete)
{
ss.deleteSheet(sheet);
}
});
}
I was using Kessy's nice script, but started having trouble when the data became very large, where the script timed out. I started looking for ways to reduce the amount of times the script read/wrote to the spreadsheet (rather than read/write one row at a time) and found this post https://stackoverflow.com/a/42633934
Using this principle and changing the loop in the script to have a loop within the loop helped reduce these calls. This means you can also avoid the second call to append rows (the "else"). My script is a little different to the examples, but basically ends something like:
`for (var i = 1; i < theEmails.length; i++) {
//Ignore blank Emails and sheets created
if (theEmails[i][0] !== "" && !completedSheets.includes(theEmails[i][0])) {
//Set the Sheet name = email address. Index the sheets so they appear last.
var currentSheet = theWorkbook.insertSheet(theEmails[i][0],4+i);
//append the header
//To avoid pasting formulas, we have to paste contents
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
//Now here find all the rows containing the same email address and append them
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++)
{
if(rows[j][0] == theEmails[i][0]) {
theNewRows[b]=[];//Initial new array
theNewRows[b].push(rows[j][0],rows[j][1],rows[j][2],rows[j][3],rows[j][4],rows[j][5],rows[j][6],rows[j][7]);
b++;
}
}var outrng = currentSheet.getRange(2,1,theNewRows.length,8); //Make the output range the same size as the output array
outrng.setValues(theNewRows);
I found a table of ~1000 rows timed out, but with the new script took 6.5 secs. It might not be very neat, as I only dabble in script, but perhaps it helps.
I have done this script that successfully gets each room and creates a new sheet with the corresponding room name and adding all the rows with the same room.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
//Set the first row as the header
var header = rows[0];
//Store the rooms already created
var completedRooms = []
//The last created room
var last = columnRoom[1][0]
for (var i = 1; i < columnRoom.length; i++) {
//Check if the room is already done, if not go in and create the sheet
if(!completedRooms.includes(columnRoom[i][0])) {
//Set the Sheet name = room (except if there is no name, then = No Room)
if (columnRoom[i][0] === "") {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("No Room");
} else {
var currentSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(columnRoom[i][0]);
}
//append the header
currentSheet.appendRow(header);
currentSheet.appendRow(rows[i]);
completedRooms.push(columnRoom[i][0])
last = columnRoom[i][0]
} else if (last == columnRoom[i][0]) {
// If the room's sheet is created append the row to the sheet
var currentSheet = SpreadsheetApp.getActiveSpreadsheet()
currentSheet.appendRow(rows[i]);
}
}
}
Please test it and don't hesitate to comment for improvements.

Merging google sheets: match with specific col values and merge data in 2 different sheets

I would like to ask if there is any possible way to set up a simple version of app script that is similar to the below add-ons.
https://chrome.google.com/webstore/detail/merge-sheets/gdmgbiccnalapanbededmeiadjfbfhkl?utm_source=ablebits
Basically, I've got sheet 1 and sheet 3, both sheets will have a common key column with specific values in each cell, and I would like to map both sheets with the data in that column then update the whole row data in the other sheets (For example, if i updated the sheet 3 then it map that col value in sheet 1, then paste the data in the corresponding row)
I have came up with a code that runs but no changes have been made, could anyone please advise how can I modify it to a simple version similar to the above add-ons? Thanks in advance.
I would like to populate the date from sheet 3 to sheet 1 after the code run, while the data from col C is matched in both sheets, please see example below, thanks!
For example, the data in sheet 1 highlighted row is having its key col with col C for common lookup value with sheet 3 while the row sequence is diff with sheet 3 (Please see the next photo, thanks!)
As you can see in sheet 3, the data of the whole row is inserted to the correct row according to the col C key col value which matched with sheet 1.
function myFunction2(){
// Get your spreadsheet and the sheets "TB" and "2"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
// Get the range on sheet "TB"
var tabsheet1 = sheet1.getRange(2, 2, sheet1.getLastRow(),
sheet1.getLastColumn());
// Get the values to compare
var datasheet1 = tabsheet1.getValues();
var datasheet3 = sheet3.getRange(2, 2, sheet3.getLastRow(), sheet3.getLastColumn());
for(var i = 0; i < datasheet1.length; i++){
for(var j = 0; j < datasheet3.length; j++){
// Compare data: if they're the same, put the value
if(datasheet1[i][0]=(datasheet3[j][0]) == 0){
//if(datasheet1[i][0].localeCompare(datasheet3[j][0]) == 0){
datasheet1[i][1] = datasheet3[j][1];
}
}
}
// Take the modified tab and put it on the spreadsheet
tabsheet1.setValues(datasheet1);
}
You want to copy the data of sheet 3 to sheet 1. For this situation, when the values of column C of sheet 3 and sheet 1 is the same, you want to copy the row of sheet 3 to sheet 1, because the order of rows for sheet 3 and sheet 1 are different. If my understanding is correct, how about this modification?
Modification points :
In order to retrieve the values from column C to last column, it uses getRange(2, 3, sheet1.getLastRow(), sheet1.getLastColumn()).
In your script, datasheet3 is the range.
About if(datasheet1[i][0]=(datasheet3[j][0]) == 0){, in order to comare the date, I used getDisplayValues().
By this, the values retrieved as a string are compared.
Modified script :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow(), sheet1.getLastColumn());
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow(), sheet3.getLastColumn()); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
datasheet1[i] = datasheet3[j]; // Modified
}
}
}
tabsheet1.setValues(datasheet1);
}
Reference :
getDisplayValues()
If I misunderstand your question, please tell me. I would like to modify it. At that time, in order to modify, can you share the sample sheet? Of course, please remove your private information from it.
Edit 1 :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Sheet 1");
var sheet3 = ss.getSheetByName("Sheet 3");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow() - 1, sheet1.getLastColumn() - 2); // Modified
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow() - 1, sheet3.getLastColumn() - 2); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
datasheet3[j].push(""); // Modified
datasheet1[i] = datasheet3[j]; // Modified
}
}
}
tabsheet1.setValues(datasheet1);
}
Edit 2 :
function myFunction2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("comment_log");
var sheet3 = ss.getSheetByName("CM");
var tabsheet1 = sheet1.getRange(2, 3, sheet1.getLastRow() - 1, sheet1.getLastColumn() - 2); // Modified
var datasheet1 = tabsheet1.getValues();
var datasheet1d = tabsheet1.getDisplayValues(); // Added
var tabsheet3 = sheet3.getRange(2, 3, sheet3.getLastRow() - 1, sheet3.getLastColumn() - 2); // Added
var datasheet3 = tabsheet3.getValues(); // Modified
var datasheet3d = tabsheet3.getDisplayValues(); // Added
for (var i = 0; i < datasheet1.length; i++) {
for (var j = 0; j < datasheet3.length; j++) {
if (datasheet1d[i][0] == datasheet3d[j][0]) { // Modified
if (datasheet1[i].length != datasheet3[j].length) {
for (var k = 0; k < datasheet1[i].length - datasheet3[j].length; k++) {
datasheet3[j].push(datasheet1[i][datasheet1[i].length - 1]);
}
}
datasheet1[i] = datasheet3[j]
}
}
}
tabsheet1.setValues(datasheet1);
}

how to write a function equivalent to ctrl + r in app script(google spread sheet)

so i am to create a doc letting the users enter the random function themselves and upon clicking a button I need to have those block where the random number is generated, updated and get the average and record the average number in a different range of block.
function run2Button(){
var sheet = SpreadsheetApp.getActiveSheet();
var range1 = sheet.getRange("b4:q7");
var range2 = sheet.getRange("b17:e136");
var range3 = sheet.getRange("b8:q8");
for (var i = 1; i<=120; i ++){
for (var col = 1; col <= range1.getWidth(); col++) {
for (var row = 1; row <= range1.getHeight(); row++) {
var number = Math.floor(Math.random() * 6) + 1;
range1.getCell(row, col).setValue(number);
}
}
var n2 = range3.getCell(1, 1).getValue();
var n9 = range3.getCell(1, 4).getValue();
var n36 = range3.getCell(1, 9).getValue();
var n64 = range3.getCell(1,16).getValue();
range2.getCell(i, 1).setValue(n2);
range2.getCell(i, 2).setValue(n9);
range2.getCell(i, 3).setValue(n36);
range2.getCell(i, 4).setValue(n64);
}
};
formatting somewhat sloppy. Initially I plan to do the random number generator myself but ultimately let them to enter the command: '=randbetween(1,6) '
themselves, but I need to, via app script, recalculate or refresh the cell how can I do that.

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.