I am building a script to copy a row in another spreadsheet. The idea is to have one sheet with the inputs that are going to be stored in a second spreadsheet. However, I am facing some real struggle in building the dynamic paste range. This is the point I was able to reach with my present knowledge:
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopy = source.getRange('bb').getValues();
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange('bb').setValues(dataToCopy)
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('yyy').getRange('bb');
Clean.clear({contentsOnly:true});
}
This script copy a range into a fixed range in a second spreadsheet, and it clears the values present in the source. My question is: How can I create a range that makes the script paste the data in the first blank row in the second spreadsheet?
I tried some combination of appendRow, getLastRow, insertRowAfter, but I was not able to get it done.
Thank you for your time!
This is what I was able to achieve with the help of a friend:
function Export2() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("1UfKqXCMNIbjh3ge7s26SNkXyGez-bY3fvl6_3-RQKos");
var source = ss.getSheetByName('Sheet26');
var dataToCopy = source.getRange('A1:E1');
var copyToSheet = database.getSheetByName("TOT");
var lastRow = copyToSheet.getLastRow();
for (var i = 1; i<6 ;i++){
var Paste = copyToSheet.getRange(lastRow + 1,i).setValue(dataToCopy.getCell(1, i).getValue());
}
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet26').getRange('A1:E1');
Clean.clear({contentsOnly:true});
}
Below is a script that will do the thing you want to do dynamically without the use of named ranges. It assumes that all the data on the source sheet should be copied over to the destination sheet. Let me know if you need any additional explanation beyond what is provided in the comments.
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopyRng = source.getDataRange(); //Gets range object of all data on source sheet
var dataToCopy = dataToCopyRng.getValues(); //Gets the values of the source range in a 2 dimensional array
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange(copyToSheet.getLastRow()+1,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy)
//Explination of the above command is as follows getRange(row, column, numRows, numColumns)
//--row is copyToSheet.getLastRow()+1 -- finds last row with content and adds one
//--Column is 1 for Column A
//--numRows is the length of the array (how many rows are in the array of values)
//--numcolumns is the length of the first element of the 2 dimensional array (arrays start at zero). The length of the first element is how many columns are in the array
//--combine the above with .getRange and you get the range object that is an exact match to the array of source data rows and columns
//--then you simply set the values of this range with the source data
dataToCopyRng.clear({contentsOnly:true});
}
You can learn more about 2 dimensional arrays here.
Cheers!
Related
I'm trying to automate hyperlink creations on my GSheet.
Here's my script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C");
var ticketID = range.getValue();
Logger.log(ticketID);
for(i = 0; i < ticketID.length; i++){
if(ticketID.length === 0){
ticketID.setValue('')
} else if(ticketID.length > 4){
ticketID.setValue('=HYPERLINK' + '("https://mylink.com/'+ticketID+'";'+ticketID+')');
}
}
}
It does nothing but when I change ticketID.setValue by sheet.getRange("C2:C").setValue it put the whole range in the url. We can see with Logger.log(ticketID) that the whole range is selected.
So according to this result, i'm missing how to get the value of each cell individualy in the range and then check if they are long enought to create an individual url. Do I need to use something like range[i] somewhere? I'm lost.
I believe your goal as follows.
You want to retrieve the values from the cells "C2:C".
When the length of value is more than 4, you want to create a formula of HYPERLINK.
When the length of value is less than 4, you don't want to put the formula.
You want to put the formulas to the cells "C2:C".
Modification points:
When range of var range = sheet.getRange("C2:C") is used, the value of var ticketID = range.getValue() is the value of cell "C2". When you want to retrieve values from the cells "C2:C", please use getValues instead of getValue.
In this case, the retrieved value is 2 dimensional array.
When range.getValue() is the string value, ticketID of var ticketID = range.getValue() is also the string. So I think that when ticketID.setValue('##') is run, an error occurs.
In your script, setValue is used in a loop. In this case, the process cost will become high.
And, when sheet.getRange("C2:C" + sheet.getLastRow()) is used instead of sheet.getRange("C2:C"), the process cost will become low a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C" + sheet.getLastRow());
var ticketIDs = range.getValues();
var values = ticketIDs.map(([c]) => [c.toString().length > 4 ? `=HYPERLINK("https://mylink.com/${c}";"${c}")` : c]);
range.setValues(values);
}
In this modification, the values are retrieved from the cells of "C2:C" + sheet.getLastRow(), and an array including the formulas and values is created, and then, the array is put to the cells.
And I used the template literal for creating the formula.
Note:
In this case, please use this script with enabling V8 runtime.
References:
getLastRow()
getValues()
map()
Template literals
You just need to apply the HYPERLINK operation to the tickets that their length is more than 4. To achieve that, you can use map() to iterate over all the elements in your list.
Solution:
function ticketURLGenerator() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
const ticketR = sheet.getRange("C2:C"+sheet.getLastRow());
const ticketIDs = ticketR.getDisplayValues().flat();
const hLinks = ticketIDs.map(ti=>{
if(ti.length>4) {
return [`=HYPERLINK("https://mylink.com/${ti}"; ${ti})`]}
else {return [ti]}
})
ticketR.setValues(hLinks);
}
I have a similar situation to the one described on this question: two worksheets, with input data coming into the Feed sheet using the importxml function and a Data sheet where new rows get copied thanks to a script set to run daily.
However, the current script is creating daily duplicates. As such, I would like to adapt the answer provided on the question above so that the script checks the IDs on column F and only copies the rows with new IDs.
How should I update the section below that creates a hash to one that looks for the IDs on column F instead? Also my rows are consistent, so is it correct to assume I can just remove the relevant code lines towards the end?
The sample Google Sheet is available here.
function appendUniqueRows() {
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Get Data');
var destSheet = ss.getSheetByName('Final Data');
var sourceData = sourceSheet.getDataRange().getValues();
var destData = destSheet.getDataRange().getValues();
// Check whether destination sheet is empty
if (destData.length === 1 && "" === destData[0].join('')) {
// Empty, so ignore the phantom row
destData = [];
}
// Generate hash for comparisons
var destHash = {};
destData.forEach(function(row) {
destHash[row.join('')] = true; // could be anything
});
// Concatentate source rows to dest rows if they satisfy a uniqueness filter
var mergedData = destData.concat(sourceData.filter(function (row) {
var hashedRow = row.join('');
if (!destHash.hasOwnProperty(hashedRow)) {
// This row is unique
destHash[hashedRow] = true; // Add to hash for future comparisons
return true; // filter -> true
}
return false; // not unique, filter -> false
}));
// Check whether two data sets were the same width
var sourceWidth = (sourceData.length > 0) ? sourceData[0].length : 0;
var destWidth = (destData.length > 0) ? destData[0].length : 0;
if (sourceWidth !== destWidth) {
// Pad out all columns for the new row
var mergedWidth = Math.max(sourceWidth,destWidth);
for (var row=0; row<mergedData.length; row++) {
for (var col=mergedData[row].length; col<mergedWidth; col++)
mergedData[row].push('');
}
}
// Write merged data to destination sheet
destSheet.getRange(1, 1, mergedData.length, mergedData[0].length)
.setValues(mergedData);
}
I'm a novice in this world of Google Apps scripts, so do please let me know if I'm missing any crucial information. Thanks in advance for the help.
You want to copy the values from "Feed" sheet to "Data" sheet.
When the values are copied, you want to copy only new values which are not included in "Data" sheet.
You want to choose the new values using the values of column "F".
If my understanding for your question is correct, how about this modification? In this modification, I modified the script in your shared spreadsheet.
Modification points:
In your script, all values of "Feed" sheet are copied to "Data" sheet. So in order to choose only new values, I used the following flow.
Retrieve the values from column "F". This is used for choosing the new values.
Retrieve the new values using the values from column "F".
Put the new values to "Data" sheet.
The script which reflected above flow is as follows.
Modified script:
From:
This is your script in the shared spreadsheet. Please modify this script to below one.
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
ts.getRange(ts.getLastRow()+1, 1,50,8).setValues(data);// 49 value refers to number of rows, 8 to columns
}
To:
function Copy() {
var sss = SpreadsheetApp.openById('#####'); // this is your Spreadsheet key
var ss = sss.getSheetByName('Feed'); // this is the name of your source Sheet tab
var range = ss.getRange('A3:H52'); //assign the range you want to copy
var data = range.getValues();
var tss = SpreadsheetApp.openById('#####'); //replace with destination ID
var ts = tss.getSheetByName('Data'); //replace with destination Sheet tab name
// Below script was added.
var values = ts.getRange("F3:F").getValues().filter(String);
var copiedValues = data.filter(function(e) {return !values.some(function(f){return f[0] == e[5]}) && e.filter(String).length > 0});
ts.getRange(ts.getLastRow() + 1, 1, copiedValues.length, copiedValues[0].length).setValues(copiedValues);
}
I'm trying to retrieve a specific value from a google app script sheet. I have a sheet where it stores information. I want to loop through that sheet based on row and retrieve all values that match and meet the conditions given.
For example
if (row[4].toString().toLowerCase() == anotherRow[4]){
//then display all rows which match that specific value
// e.g:
row[4]. Display everything that matches anotherRow only;
}
this is what I can't get my head around displaying all rows that meet that criteria only, currently I'm able to display all row[4], row[4] is the column
This script that will get a range of data and store it as an array of arrays.
function getMultipleValues(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
// getRange exp. -
//sheet.getRange(first row, first column, last row, last column)
// Get a grid of data 4 rows and two columns
var range = sheet.getRange(1,1,4,2);
var dataGrid = range.getValues()
Logger.log(dataGrid)
//[["Fruit","quantity"],["pears",1],["apples",1],["grapes",3]]
//Get a column of data
var range = sheet.getRange(1,1,4,1);
var dataColumn = range.getValues()
Logger.log(dataColumn)
//[["Fruit"],["pears],["apples"],["grapes"]]
//If the column length is unknown. Get everything.
//Be sure to delete rows that are not needed in the sheet when using this.
var range = sheet.getRange("A1:A");
var allOfColumnA = range.getValues()
Logger.log(allOfColumnA)
//[["Fruit"],["pears],["apples"],["grapes"],[],[],[],[]]
}
Building on Supertopaz' answer, it looks like the filter method of an array will work to remove rows that don't match your criteria. For example, given a set of data like this:
Source data for function
The following function will match the data to the criteria you specify, and write the matches to a second sheet.
function writeMatches() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var sheet2 = ss.getSheetByName('Sheet2');
var criteria = sheet1.getRange(1,2,1,1).getValue(); // returns a single value if the range is a single cell
var arData = sheet1.getRange(4,1,5,2).getValues(); // returns a 2d array
var arOutput = [];
var rangeOutput;
arOutput = arData.filter(function(row){
return row[1].toLowerCase() === criteria
/* The callback function iterates through the array.
When this expression evaluates to true, the
element is included in the output array.
The callback function can also supply an optional
index argument, if you want to compare symmetric
arrays.
*/
});
rangeOutput = sheet2.getRange(1,1,arOutput.length,arOutput[0].length);
rangeOutput.setValues(arOutput);
}
I'd appreciate some help on my first google script if possible.
I'm trying to archive our daily processing log by copying and pasting them to the bottom of an end of day log spreadsheet.
The issue is that the number of rows processed each day will vary so I can't set a fixed number of rows to copy to the target spreadsheet.
I just want to copy from row B7 down to the last row with values entered.
function pasteprocessinglog() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Sheet_name');
var range = ss.getRange('B7:J');
var data = range.getValues();
var tss = SpreadsheetApp.openById('URL');
var ts = tss.getSheetByName('Processing Log');
ts.getRange(ts.getLastRow() + 1, 2, ss.getMaxRows(), 9).setValues(data);
}
I'm currently receiving error: Incorrect range height, was 6467 but should be 6473
I'm guessing this is because it's trying to copy the empty rows too and the spreadsheet isn't long enough.
Any help would be appreciated :)
Thank you!
In data retrieved using ss.getRange('B7:J').getValues(), empty cells are included. So the length of retrieved data is larger than that of real data. 6467 and 6473 means the length of data array and the value from getMaxRows(), respectively.
And getMaxRows() retrieves the number of most bottom cell including empty cells. So in the case for using setValues(), data can be copied by using the length of data array for setValues as numRows of getRange (https://developers.google.com/apps-script/reference/spreadsheet/sheet#getRange(Integer,Integer,Integer,Integer)
).
The script is as follows.
function pasteprocessinglog() {
var sss = SpreadsheetApp.getActiveSpreadsheet();
var ss = sss.getSheetByName('Sheet_name');
var firstrow = 7; // 7th row
var range = ss.getRange(firstrow, 2, ss.getLastRow() - firstrow + 1, 9);
var data = range.getValues();
var tss = SpreadsheetApp.openById('URL');
var ts = tss.getSheetByName('Processing Log');
ts.getRange(ts.getLastRow() + 1, 2, data.length, 9).setValues(data);
}
If my understanding have mistaken, I'm sorry.
I had success filling a single column (A) with the value found in range A1...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getDataRange().getNumRows();
var rngVal = ss.getRange("A1").getValue()
ss.getRange("A2:A"+lastRow).setValue(rngVal)
So then I thought I was on easy-street, and I tried to modify/apply that to a larger range by filling a multi-column range with the values found in range C1:H1...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getDataRange().getNumRows();
var rngVal = ss.getRange("C1:H1").getValues()
ss.getRange("C2:H"+lastRow).setValues(rngVal)
Apparently there is a bit more to this than simply slapping an "S" onto the end of the word "Value".
The error reads as follows:
Incorrect range height, was 1 but should be 10
(FYI: var lastRow = 11)
Btw, I get no error if I use Value instead of Values, although I end up with cells full of the value found only in range C1.
So I'm close.... or way off. One of those.
Help???
The error message is quite explicit... the size of the array must fit into the range in both getValues and setValues. Try it like this
:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H"+lastRow).getValues();// get an array of 10 "rows" and 6 "columns"
ss.getRange("C2:H"+(lastRow+1)).setValues(rngVal);//write back this array to a range that has the same size. (starting from Row2 it must ends on lastRow+1 to keep the same number of "rows"
}
This function will shift the range C1:H last Row to C2:H lastRow+1, not sure it is very useful but that's not the point here ;-)
EDIT : sorry, I didn't understand exactly your requirement... here is a code that reproduce data from C1:H1 in all rows below
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H1").getValues();// get an array of the first row
var rowData = rngVal[0]
var newData = []
for(n=1;n<lastRow;++n){newData.push(rowData)}
ss.getRange("C2:H"+lastRow).setValues(newData);//write back this array to a range that has the same size. (starting from Row2 it must ends on lastRow+1 to keep the same number of "rows"
}
EDIT2 following your comment below :
A word of explanation :
When using range.getValues() we get a 2 dimensions array, meaning an array of arrays that can be represented as follow : [[data1,data2,data3],[data4,data5,data6]] data1, 2 & 3 are the value of the first array (index 0) and data 4,5 & 6 are the values of the second array (index 1). So if you want to get the values in the first array you have to write it like this : value = arrayName[0] and this will return a one dimension array [data1,data2,data3], that's what I used to get rowData.
Now we need to get a 2 dimension array again to be able to write back the new data to a range in the spreadsheet. Therefor we create a new array (var newData=[] or var newData = new Array() does exactly the same), and in the for loop we add the rowData array to this new array... the result will be an array of arrays, that is actually what we were looking for and we can write this directly to the sheet in one single setValues statement.
Ok, this seems to do it...
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var lastRow = ss.getLastRow()
var rngVal = ss.getRange("C1:H1").getValues()
for (var x=1; x<=lastRow; x++) {
ss.getRange("C"+x+":H"+x).setValues(rngVal);