Find value in every cell of SpreadSheet - google-apps-script

Cannot make the code works, i want to check for every cell in spreadsheet for a value and paste the entire row that contains the value below.
var rowValue = spreadSheetData.getValues()
var findText = spreadSheetData.createTextFinder(usedEmail)
var bool
var mailFinder = findText.findAll().map(x => x.getA1Notation())
Logger.log(mailFinder)
if (choice == "Sí") {
var i = mailFinder.indexOf(usedEmail)
if (i != "") {
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
bool = true
}else{
bool = false
}
}
If i do like the code above, it works and gives me true or false depending of the result but the "i" var throws me "undefined" because i'm not using the rowValue variable. In the other hand if i add a "for" iteration like:
for(i = 0; i < rowValue.length; i++){
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
}
It copies the last row and that's not the approach i want because i want to copy the row where the value is.
Example sheets:
| header1 | header2 |
| -------- | ------------------------- |
| 231232132| valueiwanttofind#gmail.com|
| asdasdas | row |
| header1 | header2 |
| ------------------------- | ------------------------- |
| 231232132 | row |
| valueiwanttofind#gmail.com| another row |
the valueiwanttofind#gmail.com can be in any cell. I tried another approach but without luck...
for(var i = 1; i < rowValue.length; i++) {
for(var n = 1; n < rowValue[i].length; n++) {
var findText = rowValue[i]
Logger.log(findText)
if (choice == "Sí") {
if (findText.indexOf(usedEmail)) {
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
bool = true
}else{
bool = false
}
}
}
}
UPDATE:
Thanks for your help, i managed to implement what i needed, i will explain briefly so if anyone comes with the same problem
function getItems(email,usedEmail, choice, name, gender){ //This functions brings vars as parameters from main function who map every row
var spreadSheet = SpreadsheetApp.openById(form.getDestinationId())
var spreadSheetData = spreadSheet.getDataRange()
var rowValue = spreadSheetData.getValues()
var bool
rowResult = rowValue.find(r => r.includes(usedEmail)) //I didn't know how to use find, instead i used findAll with textfinder
if (choice == "Sí") {
if (rowResult) {
var rowValues = rowResult //pass rowValues with the result above
bool = true
}else{
bool = false
}
}
for(i = 0; i < rowValue.length; i++){
rowValue[i][0] = new Date() //I used just in case to put a the current date when the form is sent to not copy that value(overwrite).
}
Logger.log(usedEmail)
Logger.log(bool)
if (bool == true){
setItems(spreadSheet, rowValues)
//RespuestaAutomatica(name, email, gender) is another function i have
Logger.log("Inscripción válida")
}else{
Logger.log("Inscripción fallida, email no registrado")
}
}
function setItems(spreadSheet, rowValues){ //the function created to append the rows at the next one.
spreadSheet.appendRow(rowValues)
}
Thanks for your help!

SUGGESTION:
Although it is still unclear what's the specific purpose this line of code below, I'm hunching that your main goal is to get the row numbers where matches are found on your sheet. But feel free to let us know if we misunderstood your post.
Line of code that is unclear:
rowValue[i][0] = new Date()
var rowValues = rowValue[i]
Why are you assigning a date value on an array data in rowValue[i][0]? What's the specific purpose of this?
Perhaps you can try this tweaked script:
//Initialized these lines below for the code to work
var spreadSheetData = SpreadsheetApp.getActiveSpreadsheet();
var usedEmail = "valueiwanttofind#gmail.com";
var choice = "Sí";
function testFunction() {
var rowValue = spreadSheetData.getDataRange().getValues();
var findText = spreadSheetData.createTextFinder(usedEmail);
var bool;
var mailFinder = findText.findAll().map(x => x.getA1Notation().toString())
if (choice == "Sí" & mailFinder.length != 0) {//Make sure mailfinder is not null
var rowValues = mailFinder;
rowValues = rowValues.map(function(x){ return x.replace(/[^abc]/,'') }); //Get the row numbers of the macthed rows that are orginally in A1Notation format
Logger.log("Found \""+""+usedEmail+"\" on Row #:\n" + rowValues);
bool = true;
}else{// If mailFinder has no data found
bool = false
}
Logger.log(bool);
}
Sample Result:
Test Sheet that I have used:

Something like this?
function myFunction() {
var email = 'valueiwanttofind#gmail.com'; // the wanted email
var sheet = SpreadsheetApp.getActiveSheet(); // the current sheet
var data = sheet.getDataRange().getValues(); // all the data from the sheet
var row = data.find(r => r.includes(email)); // the row with the email or undefined
if (row) sheet.appendRow(row); // append the row at the end of the sheet
}
It copies (appends) the first row that contains the email at the end of the sheet.
(If you want the index of the row, you can use findIndex() instead of find())
If you want to append all the rows that contain the email you can use filter() instead of find():
function myFunction() {
var email = 'valueiwanttofind#gmail.com'; // the wanted email
var sheet = SpreadsheetApp.getActiveSheet(); // the current sheet
var data = sheet.getDataRange().getValues(); // all the data from the sheet
var rows = data.filter(r => r.includes(email)); // the rows with the email
// append the array of rows at the end of the sheet
if (rows) rows.forEach(row => {
sheet.appendRow(row); // <--- it's ok if there are a few rows only
SpreadsheetApp.flush();
});
}
(The script should be improved if you have many rows with the email).

Related

setValue of filtered row cell in google apps script

I'm looking for some guidance with a Google Apps Script I'm trying to write.
I have a Google Sheet containing thousands of links. I need to check if the links are still 'Active' and if not marked them as 'Closed'. To save iterating over every single row I'm first filtering the rows to get all of the links that have 'Active' set in row[9]. I'm then using fetch to get the response code and based on that code update the value of row[9] to either 'Still Active' or 'Closed'
I know I can use as a concept in any other case:
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sites").getRange().setValue();
However, I'm finding it difficult to use this in conjunction with the filteredRows I need.
The sheet looks a little like this:
| date | name | link | status |
|------------|--------- |---------------------------|--------|
| 2021-08-24 | Google | https://www.google.com/ | Active |
| 2021-08-20 | Facebook | https://www.facebook.com/ | Active |
| 2021-08-18 | Twitter | https://www.twitter.com/ | Closed |
Current script:
function getStatus() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sites");
var rows = sheet.getDataRange().getValues();
var filteredRows = rows.filter(function (row) {
var status = row[9];
if (status === "Active") {
return row
}
});
filteredRows.forEach(function(row){
var link = row[3];
var status = row[9];
var options = {
'muteHttpExceptions': true,
'followRedirects': false,
'validateHttpsCertificates': false
};
var response = UrlFetchApp.fetch(link, options);
if (status === "Active") {
if (response.getResponseCode() == 200) {
row.status.setValue("Still Active!");
}
else {
row.status.setValue("Closed!");
}
}
})
}
I'd propose to use one more array indexes that will contain positions of filtered rows:
var indexes = [];
var filteredRows = rows.filter(function (row, i) {
var status = row[9];
if (status === "Active") {
indexes.push(i);
return row;
}
});
Then you can change the rows in your main array this way:
indexes.forEach((x,i) => rows[x][9] = filteredRows[i].status);
But actually I don't understand why do you need to filter the rows? You can just skip the rows that have no 'Active' in column [9].
Or you're trying to use setValues() for every processed row? It's a really bad idea if you have thousands rows. You need to process the 2d array (a whole) and set all the thousands rows at once:
sheet.getDataRange().setValues(rows);
So after all your code could look like this:
function getStatus() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sites");
var rows = sheet.getDataRange().getValues();
var options = {
'muteHttpExceptions': true,
'followRedirects': false,
'validateHttpsCertificates': false
};
for (let i in rows) {
if (rows[i][9] != 'Active') continue; // skip the row if not 'Active'
var response = UrlFetchApp.fetch(rows[i][3], options);
if (response.getResponseCode() == 200) {
rows[i][9] = "Still Active!";
} else {
rows[i][9] = "Closed!";
}
}
sheet.getDataRange().getValues(rows); // set all values back to the sheet
}
Though, if you have thousands rows the script can excess the time limit. It will need another tricks then.
Probably it will work. Slowly (due setValue() for every changed row) but you can run it several times:
function getStatus() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sites");
var rows = sheet.getDataRange().getValues();
var indexes = [];
var filteredRows = rows.filter(function (row, i) {
var status = row[9];
if (status === "Active") {
indexes.push(i+1); // save the index of filtered row
return row;
}
});
var options = {
'muteHttpExceptions': true,
'followRedirects': false,
'validateHttpsCertificates': false
};
filteredRows.forEach(function (row, i) {
var link = row[3];
var status = row[9];
var response = UrlFetchApp.fetch(link, options);
if (response.getResponseCode() == 200) {
sheet.getRange(indexes[i],10).setValue("Still Active!");
} else {
sheet.getRange(indexes[i],10).setValue("Closed!");
}
})
}

loop through 2 google sheets

Goodday,
I have 2 sheets. For each ID on sheet1 I need to verify if that ID is also on sheet2 AND if Date Processed is blank.
If both condition are true > today's date should be set in Date Processed.
I've managed to do so for just 1 value (A2) from sheet1.
What I need is a way to go through all the values in sheet1. Ideally the row in sheet1 would also get deleted (not sure if that is possible)
This is my code till now
function myMatch(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var ss = file.getSheetByName("Sheet1");
var ws = file.getSheetByName("Sheet2");
var wsData = ws.getDataRange().getValues();
var mySearch = ss.getRange("A2").getValue();
for(var i = 0; i < wsData.length; i++){
if(wsData[i][1] == mySearch && wsData[i][2] == "")
{
ws.getRange(i+1,3).setNumberFormat('dd"-"mmm"-"yyyy').setValue(new Date());
}
}
}
Your help is really appreciated as I have been trying and searching for a solution for 2 days now. Thank you
I know it doesn't makes much sense. Muhammet's code works and looks just fine. But, rather for fun and educational purposes, here is another "functional" solution:
function myFunction() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const s1 = ss.getSheetByName('Sheet1');
const s2 = ss.getSheetByName('Sheet2');
// get data from Sheet1 and Sheet2
const s1_data = s1.getDataRange().getValues().slice(1,);
const s2_data = s2.getDataRange().getValues().slice(1,);
// get IDs from data of Sheet1
const IDs = s1_data.map(x => x[0]);
const IDs_to_delete = []; // here will be IDs to delete
// function checks and fill a row and adds ID to array to delete
const write_date = (id,row) => {
if (row[1] == id && row[2] == '') {
IDs_to_delete.push(id);
row[2] = new Date();
}
}
// change rows within data of Sheet 2
IDs.forEach(id => s2_data.forEach(row => row = write_date(id,row)));
// clear and fill Sheet 2
s2.getDataRange().offset(1,0).clearContent();
s2.getRange(2,1,s2_data.length,s2_data[0].length).setValues(s2_data);
// remove rows from data of Sheet 1
const s1_data_new = s1_data.filter(row => !IDs_to_delete.includes(row[0]));
// clear and fill Sheet 1 with new data
s1.getDataRange().offset(1,0).clearContent();
s1.getRange(2,1,s1_data_new.length,s1_data_new[0].length).setValues(s1_data_new);
}
The only improvements over Muhamed's code is that this implementation removes processed rows from Sheet1. And, I believe, it will work faster for huge lists, because it doesn't use getRange() and setValue() on every found cell but fills all cells of the sheet at once with setValues() method.
You need a loop for this. Use
var mySearchs = ss.getRange('A2:A').getValues();
and loop through all values of this array.
function myMatch(){
var file = SpreadsheetApp.getActiveSpreadsheet();
var ss = file.getSheetByName("Sheet1");
var ws = file.getSheetByName("Sheet2");
var wsData = ws.getDataRange().getValues();
var mySearchs = ss.getRange('A2:A').getValues();
mySearchs.forEach((v) => {
for(var i = 0; i < wsData.length; i++){
if(wsData[i][1] == v && wsData[i][2] == "")
{
ws.getRange(i+1,3).setNumberFormat('dd"-"mmm"-"yyyy').setValue(new Date());
}
}
})
}

Google Sheets Script: Insert New Row In Named Range

I use Named Ranges a lot, for sheets Data Validation etc. I'm adding 'Saved Comparisons' (cell H3) to a stock comparison sheet:
Although untested yet, I've almost created a function to Save or Update the current comparison to a Named Range Compare_Saved:
However, I'm stuck at how to add a New Row to the Named Range, and/or then select the required Row in the Range to update it. The problem is described in the indented section of the code below, commented with // ***** (may need to scroll down to see it):
function compareSaveNew() { compareSave('new'); } // button call
function compareSaveUpdate() { compareSave('update'); } // button call
function compareSave(mode){
const srcSheet = 'Compare';
const dataSheet = 'Data';
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sSrc = ss.getSheetByName(srcSheet);
const sData = ss.getSheetByName(dataSheet);
const ui = SpreadsheetApp.getUi();
var promptMsg='', data, rv, rUpdate;
// Check/confirm if adding new, or updating
if ( mode == 'update' && src.getRange('Compare_SavedUsed') == 'Select Comparison' ) {
promptMsg = 'A Saved Comparison has not been selected. Cancel, or enter a New Name: ';
} else if ( mode == 'new' && src.getRange('Compare_SavedUsed') != 'Select Comparison' ) {
promptMsg = 'A Saved Comparison has been selected. Cancel and use [UPDATE],\nOR\n' +
'enter a New Name if you want to Save a new Comparison based on this one: ';
} else {
promptMsg = 'New Name for Saved Comparison: ';
}
var newName = ui.prompt('New Name for Saved Comparison: ');
if (!newName) {return;} // Exit, Save/Update cancelled
// Check newName is unique
data = sData.getRange('Compare_Saved');
for(var i = 0; i<data.length;i++){
if( data[i][0] == newName ) { rv = true; break; }
}
if (rv) {ui.alert('"' + newName + '" aleady exists.'); return;} // Exit, new name exists
// *****
// Create New row in Named Range
if (mode=='new') {
// add row to range - how ???
}
// Get new/existing row as range from Named Range to update
// var rUpdate = sData.getRange( ??? );
// *****
var newSave=[];
newSave.push([
newName,
'', // left blank, so name can overflow to this col in Data sheet
sSrc.getRange('Compare_Period'),
sSrc.getRange('Compare_Frequency'),
sSrc.getRange('Compare_From'),
sSrc.getRange('Compare_To'),
sSrc.getRange('Compare_Index'),
sSrc.getRange('Compare_Stock1'),
sSrc.getRange('Compare_Stock2'),
sSrc.getRange('Compare_Stock3'),
sSrc.getRange('Compare_Stock4')
]);
rUpdate.setValues(newSave);
return true;
}
Apologies if this post is a bit verbose, but any pointers would be appreciated.
How about remove() and addNew() namedRanges
Adapted code I found by #Cooper (thanks!) for my specific needs, which returns new row num, or row num to be updated, depending on mode:
function compareAddOrFindRowInNR(namedRange,newName,mode){
// Purpose: Add a new row in Named Range and return row num, OR return existing row num
// Credit: #Cooper at https://stackoverflow.com/a/60011251/190925
const ss = SpreadsheetApp.getActiveSpreadsheet();
// Get the NR
var nrAll = ss.getNamedRanges()
for (var i=0; i<nrAll.length; i++) {
if( nrAll[i].getName() == namedRange ) {
var nr = nrAll[i];
var h = nr.getRange().getHeight(); Logger.log('h: '+h)
var row= nr.getRange().getRow(); Logger.log('row: '+row)
var w = nr.getRange().getWidth(); Logger.log('w: '+w)
var col= nr.getRange().getColumn(); Logger.log('col: '+col)
var sh = nr.getRange().getSheet(); Logger.log('sh: '+sh)
// Create new row in NR, or find existing row
if (mode=='new') {
var updateNR = sh.getRange(row,col,h+1,w);
ss.setNamedRange(namedRange,updateNR); // doesn't set Sheet?
var rowUpdate = row+h;
} else {
data = nr.getRange().getValues();
for (var i=0; i<data.length; i++) {
Logger.log(data[i][0]);
if( data[i][0] == newName ) {
var rowUpdate = row+i;
break;
}
}
}
break;
}
}
Logger.log('rowUpdate: '+rowUpdate);
return rowUpdate;
}

Split a Google Sheet into multiple tabs based on column values

I used a previous answer (thanks kessy!) to split 7000 or so rows into 40 or so different tabs based upon values in a column. I ran the same script on another nearly identical file and I get the error "TypeError: Cannot read property 'getRange' of null (line 5, file "Code")". I tried with a greatly simplified file and get the same error. Any help getting this to work is very much appreciated.
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]);
}
}
}
I am not sure what is exactly your goal, but based on the error message you are getting it seems that you are not getting the active sheet properly. Instead, I would suggest you to specify the sheet by its name. Let's assume the desired name of the sheet you want to get is Sheet1. Then, in the first line of your function you can replace this:
var sheet = SpreadsheetApp.getActiveSheet();
with this:
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
I also optimized your code a little by removing all the unnecessary SpreadsheetApp.getActiveSpreadsheet() calls:
function myFunction() {
var ss = SpreadsheetApp.openById("SpreadsheetId");
var sheet = ss.getSheetByName('Sheet1');
// This var will contain all the values from column C -> Room
var columnRoom = sheet.getRange("C:C"+sheet.getLastRow()).getValues();
// This var will contain all the rows
var rows = sheet.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 = ss.insertSheet("No Room");
} else {
var currentSheet = ss.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
sheet.appendRow(rows[i]);
}
}
}
You can also run a loop within the loop and keep things server side for a faster result (at least it worked for me, I was having trouble with long spreadsheets timing out).
You have to know how many columns you want to pass over, maybe there is a better way to push the values than I have done (I only dabble in script).
function splitSheets() {
var theWorkbook = SpreadsheetApp.getActiveSpreadsheet();
var theSheet = theWorkbook.getSheetByName("Master");
//Let's delete any sheets that were previously split, so we can rerun the script again and again
var sheets = theWorkbook.getSheets();
for (i = 0; i < sheets.length; i++) {
switch(sheets[i].getSheetName()) {
case "Master":
break;
default:
theWorkbook.deleteSheet(sheets[i]);
}
}
// This var will contain all the values from column C -> Your splitting Key
var key = theSheet.getRange("C:C").getValues();
// This var will contain all the rows
var rows = theSheet.getDataRange().getValues();
//Set the first row as the header, get the range so we can keep the formatting
var headerFormat = theSheet.getRange("2:2");
//Store the rooms already created
var completedSheets = [];
//We start at i=2 because we're on row 3, row zero for the button, row one for the header
for (var i = 2; i < key.length; i++) {
//We don't want to run the loop if we've already created the blank page and the row key is also blank.
if(completedSheets.includes('Blank') && key[i][0] === ""){
//do nothing
}else{
//Check if the room is already done, if not go in and create the sheet
if(!completedSheets.includes(key[i][0]) ) {
//Set the Sheet name = unique key (except if there is no name, then = Blank)
if (key[i][0] === "") {
var currentSheet = theWorkbook.insertSheet("Blank");
} else {
var currentSheet = theWorkbook.insertSheet(key[i][0]);
}
//To avoid pasting formulas, we have to paste contents, copying allows us to keep formatting
headerFormat.copyTo(currentSheet.getRange(1,1),{contentsOnly:true});
headerFormat.copyTo(currentSheet.getRange(1,1),{formatOnly:true});
//Now here find all the rows containing the same key address and push them, this way doing it server side
var theNewRows =[];
var b=0;
for(var j = 1; j < rows.length; j++) {
if((rows[j][2] == key[i][0]) || (rows[j][2] === '' && currentSheet.getName() == "Blank")){
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],rows[j][8]);
b++;
}
}
var outrng = currentSheet.getRange(2,1,theNewRows.length,9);//Make the output range the same size as the output array
outrng.setValues(theNewRows);
//The new sheet name gets added to the completed sheets list and the value of var last is updated in prep of next step
if(currentSheet.getSheetName() == 'Blank') {
completedSheets.push('Blank');
last = "Blank";
}else{
completedSheets.push(key[i][0])
last = key[i][0]
}
}
}
}
//And return to the Master
SpreadsheetApp.setActiveSheet(theWorkbook.getSheetByName('Master'));
}
Example here, just click the button on the page
https://docs.google.com/spreadsheets/d/1pfeU2CFDbZbA4O0b4z80l5MyCKDNQnUdkpKlzODbAiI/edit?usp=sharing
It's not perfect, but hope it helps.

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.