Google Sheets - Modify Code To Act Dynamically When Using Slice - google-apps-script

I am using the splice Script to run code over numerous tabs
function Slice_TABS() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets().slice(7); // get 8th sheet onwards
sheets.forEach(ss=>{
Donor();
})}
and am trying to use the below code to bring filtered results up to 10 per SKU. When i name the sheet the code works fine but i need it to run on the ActiveSpreadsheet. I am getting the following error
" Type Error: sheet.getRange is not a function" (BOLD Below)
function GetNSKUSAG11() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheets();
*//Building the matching set.
**var matchingValues = sheet.getRange('AF3:AF').getValues().filter(value => value[0] !=
'').map(nonBlankValue => [nonBlankValue[0], 0]);***
//Filtering out the desired number of values
var values = sheet.getRange("LOCATIONS!$A$3:$B").getValues().filter(value =>
advancedFilter(value[0], matchingValues));
let cols = values[0].length;
let rows = values.length;
//Printing out the found rows starting from AI
sheet.getRange(2, 36, rows, cols).setValues(values);
}
function advancedFilter(value, toMatch) {
let matched = toMatch.filter(couple => couple[0] === value);
if (matched.length > 0) {
if (matched[0][1]<10) { //Here you can decide when to stop returning SKUs
matched[0][1] += 1;
return true;
}
}
return false;
}
I think I am close.

Answer to this question - thanks to Tanaike is
Change the code as per below
var sheet = ss.getSheets()
to
var sheet = ss.getActiveSheet()

Related

Loop the recored macro in google sheet

Google sheet linkSheet 1 imageI have created a crypto analysis sheet in Google sheets, where it takes the data from Investing.com.
In a sheet1 I have all the info for a single coin, now I don't want to check each one at a time.
So in sheet2, i placed the complete Coins list. I recorded a macro with copy pasting the required data from sheet1 to sheet2. Now I want to do the same for all remaining 100 coins. How to loop this. Kindly answer. I don't have knowledge in Codes but trying my best.
I tried with this below code. But while running macro, this below function not run.
function loop() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Analysis_USD');
var data = ss.getSheetByName('Screener').getRange('B3:B102').getValues();
var output = [];
for (var i = 0; i < data.length; i++) {
sheet.getRange('A2').setValue(data[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(10000);
if (sheet.getRange('B102').getValue() == 'Yes'){
output.push([data[i][0]]);
}
}
sheet.getRange(2,12,output.length, 1).setValues(output);
}
From the Coin list(sheet2) i am pasting one coin to the A2 of sheet 1 and it calculates and the result has to be paste in the sheet 2, for 1st coin i have recorded the macro
Please try to following code modification and check if it does work, as it need to Transpose your column value into row value to be able to write to Screener sheet, it work when I test it for single coin execution, the looping I did not test for it but it work in the same logic:
function loop() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Analysis_USD');
var data = ss.getSheetByName('Screener').getRange('B3:B102').getValues();
for (var i = 0; i < data.length; i++) {
sheet.getRange('A2').setValue(data[i][0]);
SpreadsheetApp.flush();
Utilities.sleep(10000);
if (sheet.getRange('B102').getValue() == 'Yes'){
var result = sheet.getRange(10,2,13,1).getValues();
}
//Transpose Column to row value
var newArray = result[0].map(function(col, i) {
return result.map(function(row) {
return row[i]
})
})
ss.getSheetByName('Screener').getRange(3 + i,4, 1,result.length).setValues(newArray);
}
}
Let's try with single row update on the Bitcoin first, because I can perform the entire row value updating using Column Value from Analysis, I am doing the testing based same data from your worksheet :
function loop() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Analysis_USD');
var data = ss.getSheetByName('Screener').getRange('B3:B102').getValues();
var result = sheet.getRange(10,2,13,1).getValues();
var newArray = result[0].map(function(col, i) {
return result.map(function(row) {
return row[i]
})
})
ss.getSheetByName('Screener').getRange(3 ,4, 1,result.length).setValues(newArray);
}

Google Sheets Script to import data based on cell value and not duplicate information

I need to pull/import data from "sheet 1" to "sheet 2" based on column 4 being a specific text string. The script should not pull lines that already exist.
I have no idea if this is possible. I can pull the data but it just recopies everything so I have duplicates.
Any help would be super appreciated.
function onEdit() {
var ss = SpreadsheetApp.openById('1Ognzsi6C0DU_ZyDLuct58f5U16sshhBpBoQ8Snk8bhc');
var sheet = ss.getSheetByName('Sheet 1');
var testrange = sheet.getRange('D:D');
var testvalue = (testrange.getValues());
var sss = SpreadsheetApp.getActive();
var csh = sss.getSheetByName('Sheet 1');
var data = [];
var j =[];
for (i=0; i<testvalue.length;i++) {
if ( testvalue[i] == 'Dan') {
data.push.apply(data,sheet.getRange(i+1,1,1,11).getValues());
j.push(i);
}
}
csh.getRange(csh.getLastRow()+1,1,data.length,data[0].length).setValues(data);
}
Sheet 1
Sheet 2
Solution
You should be able to replace your code with this and it will work. You would put this script in the target sheet (Sheet 2), and replace the ID in the first line of the function with the origin (Sheet 1).
I'll leave it up to you to change to an onEdit or to make it a menu item. Right now it can be run from the script editor. onEdit doesn't make sense to me as an appropriate trigger. Maybe you prefer a Time-Driven Trigger. Though a custom menu would be the best way IMO.
function pullData() {
var sourceSs = SpreadsheetApp.openById('[YOUR_SPREADSHEET_ID]');
var sourceRange = sourceSs.getSheetByName('Sheet1').getDataRange();
var sourceHeight = sourceRange.getHeight();
var sourceWidth = sourceRange.getWidth();
var sourceData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, sourceHeight - 1, sourceWidth).getValues();
var targetSs = SpreadsheetApp.getActive();
var targetRange = targetSs.getSheetByName('Sheet1').getDataRange();
var targetHeight = targetRange.getHeight();
var targetWidth = targetRange.getWidth();
var sourceDataChecker = [];
var targetDataChecker = [];
sourceData.forEach((row) => {
sourceDataChecker.push(row[0] + row[1] + row[2] + row[3]);
})
if (targetHeight != 1) {
var targetData = sourceSs.getSheetByName('Sheet1').getRange(2, 1, targetHeight - 1, targetWidth).getValues();
targetData.forEach((row) => {
targetDataChecker.push(row[0] + row[1] + row[2] + row[3]);
});
};
sourceData.forEach((row, i) => {
if (!(targetDataChecker.includes(sourceDataChecker[i]))) {
targetSs.appendRow(row);
};
});
}
Explanation
This script builds an "index" of each row in both sheets by concatenating all the values in the row. I did this because I noticed that sometimes you have "joe" in two rows, and so, you can't simply use column 4 as your index. You are basically checking for any row that is different from one in the target sheet (Sheet 2).
If the target sheet is blank, then all rows are copied.
References
Append Row to end of sheet
Get Data Range (range of sheet that contains data)
Get Range Height (to deal with headers)
Get Range Width
for Each

How to set Background color to a filtered row in Google Sheets using GAS/App Script

I have a google sheets file where I filter rows based on a single column criteria. if certain cell in a column is empty, I filter that entire row. But right now my problem is that I am trying to set Background color to the filtered row. But I keep getting this error: "TypeError: rows.filter(...).setBackground is not a function" when I try the following code:
var mydata3 = rows.filter(row => row[8] == '').setBackground("red");
Does anyone know how I can achieve this or is there any work around? Because I can see that setBackground is not the data type as mydata3 which is basically a filtered row.
Thanks.
var sss = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var sheetID = sss.getSheetId(); // get the id of first page
var values = sss.getDataRange().getValues();
var getRangeRow2 = sss.getDataRange().getRow()[5];
var getRangeCol2 = sss.getDataRange().getColumn()[1];
function insertRecord() {
var sheet_1 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var headerRowNumber = 3;
var rows = sheet_1.getDataRange().offset(headerRowNumber, 0, sheet_1.getLastRow() -
headerRowNumber).getValues();
var mydata3 = rows.filter(row => row[8] == '');
}
Try it this way:
function insertRecord() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheets()[0];
const rg=sh.getRange(3,1,sh.getLastRow()-2,sh.getLastColumn());
let vs=rg.getValues()
var rows=vs.map(function(r,i){
if(r[8]=='') {
sh.getRange(i+3,1,1,sh.getLastColumn()).setBackground('#ff0000');
return r;
}
}).filter(function(e){return e;});
console.log(rows);
}

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.

for each in ss.getSheets() get a range of data

I'm struggling on a child task within a function I'm working on.
For each sheet in sheets, I would like to get the data in range A16:D, combine into one big array and then out put the combined data into a new sheet. But each time I try to select each data range it comes as undefined?
How can I get the data from each sheet in sheets and then add into one big array?
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ranges = [];
function combineData() {
var activeSheetName = ss.getActiveSheet().getName();
var sheets = ss.getSheets();
sheets.forEach(function(e) {
var sheetName = e.getName();
if (sheetName != activeSheetName && sheetName != 'Report Configuration' && sheetName != 'Sheet1')
var wee_data = e.getRange('A16:D').getValues(); // TROUBLE HERE, WEE_DATA IS UNDEFINED
for(var j = 0; j<wee_data.length; j++) {
store.push(wee_data[j]);
}
ranges.push(wee_data);
});
if (ranges.length) {
Logger.log('range length is: ' + ranges.length);
adjustSheetLength(); // ensure there are enough rows in the destination sheet.
// figure out how to output data array "ranges" into a sheet "combined".
}
}
function adjustSheetLength(){
var comb = ss.getSheetByName('combined');
var lastRow = 4;
var maxRows = comb.getLastRow();
comb.deleteRows(lastRow, maxRows-lastRow);
var datapullSize = ranges.length;
comb.insertRows(4, datapullSize-2);// insert exactly the number of rows you need.
}
I think your issue is in the .forEach callback function. When I debugged those functions I got a not allowed in callback exception.
Change the foreach to a for in I think that'll fix your issue.
for( var i in sheets ) {
Logger.log(sheets[i]);
...
}