First-time poster here. I would like some insight on some Google App Script code i think could be spruced up a bit.
Long story short.
I have a 2 Google Sheet tables
A “LOCALE” spreadsheet - listing unique names of locations
A “FEED” spreadsheet - listing photo descriptions, including the locations. The same location is listed multiple times in this spreadsheet.
Both of these tables have a “Location” column, which references each other with a Key column.
The problem I want to solve for:
When I edit a location name in the “LOCALE” spreadsheet, it should automatically update all the location names in the “FEED” spreadsheet.
The way I solved this problem:
I used a for loop within a for loop for this. To summarize:
for every row in "LOCALE"...
..go through every row in "FEED"...
...If a value in the Key column in the FEED Sheet matches a value in the Key column in the LOCALE Sheet...
...but the value in the Location column in the FEED Sheet doesn't match the value in the Location column in the LOCALE Sheet...
...update the Location column in the FEED Sheet with the value in the Location column in the LOCALE Sheet.
If you're not confused yet, here's the code i wrote for it:
// for each row in the "Locale" sheet...
for(var L = LocationsRefValues.length-1;L>=0;L--) {
// for each row in the "Feed" sheet...
for(var F = FeedRefValues.length-1;F>=0;F--) {
if (FeedRefValues[F][97] == LocationsRefValues[L][17] &&
FeedRefValues[F][10] != LocationsRefValues[L][1]) {
FeedDataSheet.getRange(F+2,10+1).setValue(LocationsRefValues[L][1]);
}
}
}
Now, this code works perfectly fine, I've had no issues. However, i feel like this a bit clunky, as it takes a while to finish its edits. I'm certain there's any easier way to write this and run this code. I've heard arrays may address this situation, but i don't know how to go about that. Hence, why I'm looking for help. Can anyone assist?
Keep in mind I'm a total Google App Script beginner who got this code working through sheer dumb luck, so the simpler the solution the better. Thanks for any consideration to my problem in advance. Looking forward to hearing from you all.
This is the full function (after i made edits suggested here.)
function ModeratorStatus() {
var Data = SpreadsheetApp.getActiveSpreadsheet(); // Local Spreadsheet
var ModeratorStatusDataSheet = Data.getSheetByName("The Status (Moderators)");
var ModeratorStatusRange = ModeratorStatusDataSheet.getRange("A2:C");
var ModeratorStatusRefValues = ModeratorStatusRange.getValues();
var ModeratorDataSheet = Data.getSheetByName("The Moderator_Numbers"); // DATA "Member" sheet
//var ModeratorRefValues = ModeratorDataSheet.getRange("A2:AD").getValues();
var ModeratorStatusObj = {};
for (var MOS = ModeratorStatusRefValues.length-1; MOS>=0; MOS--) {
ModeratorStatusObj[ModeratorStatusRefValues[MOS][2]] = ModeratorStatusRefValues[MOS][0];
}
var ModeratorValues = ModeratorDataSheet.getRange("A1:AD").getValues();
for (var MO = ModeratorValues.length-1; MO >=0; MO--) { // for each row in the "Moderator" sheet...
var ModeratorVal28 = ModeratorValues[MO][28];
if (ModeratorStatusObj[ModeratorVal28] != ModeratorValues[MO][1]) {
ModeratorValues[MO][1] = ModeratorStatusObj[ModeratorVal28];
}
}
var destinationRange = ModeratorDataSheet.getRange(1, 1, ModeratorValues.length, ModeratorValues[0].length);
destinationRange.setValues(ModeratorValues);
I used the code in a different function as a test. To make it easier
LOCALE = MODERATOR STATUS
FEED = MODERATOR
If there are no duplicate [17]s with different [1]s in the LocationsRefValues, you can reduce the computational complexity from O(n ^ 2) to O(n) by creating a mapping object for LocationsRefValues beforehand, whose keys are the LocationsRefValues[L][17]s and whose values are the LocationsRefValues[L][1]s:
var locationObj = {};
for (var L = 0; L < LocationsRefValues.length; L++) {
locationObj[LocationsRefValues[L][17]] = LocationsRefValues[L][1];
}
for (var F = FeedRefValues.length - 1; F >= 0; F--) { // for each row in the "Feed" sheet...
var feedVal97 = FeedRefValues[F][97];
if (locationObj[feedVal97] != FeedRefValues[F][10]) {
FeedDataSheet.getRange(F + 2, 10 + 1).setValue(locationObj[feedVal97]);
}
}
Thanks #TheMaster, you can speed this up by calling setValue only once, at the end, rather than calling it in a loop, probably something along the lines of:
var locationObj = {};
for (var L = 0; L < LocationsRefValues.length; L++) {
locationObj[LocationsRefValues[L][17]] = LocationsRefValues[L][1];
}
var feedValues = FeedDataSheet.getValues();
for (var F = FeedRefValues.length - 1; F >= 0; F--) { // for each row in the "Feed" sheet...
var feedVal97 = FeedRefValues[F][97];
if (locationObj[feedVal97] != FeedRefValues[F][10]) {
feedValues[F + 2][10 + 1] = locationObj[feedVal97];
}
}
var destinationRange = ss.getRange(1, 1, feedValues.length, feedValues[0].length);
destinationRange.setValues(feedValues);
you can use onEdit(e) trigger to get the reference to the edited cell. In that case you won't need to iterate over the entire Locale" sheet:
function onEdit(e) {
var range = e.range; // edited cell
var rowIndex = range.getRow()
var colIndex = range.getColumn()
if (rowIndex >= LocaleRange.startRow && rowIndex <= LocaleRange.EndRow &&
colIndex >= LocaleRange.startColumn && colIndex <= LocaleRange.EndColumn) {
var index = rowIndex - LocaleRange.startRow
var keyValue = LocationsRefValues[index][17]
var newLocValue = range.getValue()
var newFeedValues = FeedRefValues.map(function (row) {
return (row[97] == keyValue) newLocValue ? : row[10]
})
FeedDataRange.setValues(newFeedValues)
}
}
Here are docs on using onEdit trigger: https://developers.google.com/apps-script/guides/triggers
I am trying to figure out how to pull only specific rows and columns using Google apps script. The =QUERY is not an option to use. Need to keep all of the logic in the script - my actual data set is quite large. To illustrate what I'm trying to solve. I have a little table of test data. TestData From which I only want columns 2,3,5 (zero based index) and only the rows with "fur".
function testFour(sheetID, fromTabName, toTabName) {
var sourceTab = SpreadsheetApp.openById(sheetID).getSheetByName(fromTabName);
var values = sourceTab.getDataRange().getValues();
var columns = [2,3,5]; //only want these columns
var output = new Array();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[0].length; j++) {
if (values[i][4] == "fur") { // only want these rows
if (j in columns) {
output.push(values[i][j]);
}
}
}
}
var targetTab = SpreadsheetApp.openById(sheetID).getSheetByName(toTabName);
targetTab.getRange(1, 1, output.length, columns.length).setValues(output);
}
This code bit works but it is not very maintainable if there are many columns:
for(n=0; n < values.length; ++n) {
var columns = [2,3,5];
if (values[n][4] === 'fur') {
output.push( [values[n][2], values[n][3], values[n][5]]);
}
When I run the testFour function I get an error about "Cannot covert Array to Object[][] on this line of the code:
targetTab.getRange(1, 1, output.length, columns.length).setValues(output);
Appreciate any help. I was really surprised to not have found any examples of this. Every solution was using the =QUERY on the sheet.
Based on Cooper's suggestion above this was how I was able to get this to work:
function testFourNew(sheetID, fromTabName, toTabName) {
var sourceTab = SpreadsheetApp.openById(sheetID).getSheetByName(fromTabName);
var targetTab = SpreadsheetApp.openById(sheetID).getSheetByName(toTabName);
var srcrg = sourceTab.getDataRange();
var srcvA = srcrg.getValues();
var desvA=[];
// var columns = [2,3,5]; //only want these columns
var columns = String("2,3,5").split(','); //only want these columns
var tstVal = "fur";
var tstCol = 4;
for (var i=0;i<srcvA.length;i++) {
var tA=[];
if (srcvA[i][tstCol] ==tstVal) {
for (var j=0;j<columns.length;j++) {
//Logger.log(srcvA[i][columns[j]]);
tA.push(srcvA[i][columns[j]]);
}
desvA.push(tA);
}
}
targetTab.getRange(1, 1, desvA.length, desvA[0].length).setValues(desvA);
}
Thank you Cooper your direction and suggestions!
EDIT:
Don't need [] around output if using this line to push to output -
output.push( [values[n][2], values[n][3], values[n][5]]);
To set values by row, data has to be in this format -
[ row,
row ]
or,
[ [1,2,3],
[4,5,6] ]
Cannot covert Array to Object[][] error is shown when range and value format do not match.
Try this line -
targetTab.getRange(1, 1, output.length, columns.length).setValues([output]);
Report Builder
I'm guessing that you want to use this function to build different reports from the same dataset. So I might try something like this:
The Function:
function testFour(t4Obj) {
var ss=SpreadsheetApp.openById(t4Obj.ssId);
var srcsh=ss.getSheetByName(t4Obj.srcShName);
var dessh=ss.getSheetByName(t4Obj.desShName);
var colA=String(t4Obj.zbcols).split('~~~');
var tstCol=Number(t4Obj.zbtstCol);
var tstVal=t4Obj.tstVal;
var srcrg=srchsh.getDataRange();
var srcvA=srcrg.getValues();
var desvA=[];
for (var i=0;i<srcvA.length;i++) {
var tA=[];
if (srcVa[i][tstCol]==tstVal) {
for (var j=0;j<colA.length;j++) {
tA.push(colA[j]);
}
desvA.push(tA);
}
}
dessh.getRange(1, 1, desvA.length, desvA[0].length).setValues(desvA);
}
The t4Obj Data Table:
Of course you would have to build yourself a function that loads tthe t4Obj from the above table. From looking at your code, I'd guess that will be no problem for you.
And please note I have not tested any of this code so it is extremely unlikely to work the first time out of the box. You can sharpen your debugging skills on it.
Let's try making a array for a square range where the number incremented in by one in the same column position down each row. The array would look like this:
var A=[[1,6,11,16,21],[2,7,12,17,22],[3,8,13,18,23],[4,9,14,19,24][5,10,15,20,25]];
Make an Array by hand
function makeArray() {
var A=[[1,6,11,16,21],[2,7,12,17,22],[3,8,13,18,23],[4,9,14,19,24],[5,10,15,20,25]];
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg=sh.getRange(5,5,5,5);//could be this way
//var rg=sh.getRange(5,5,A.length,A[0].length);//or in most programs this way
rg.setValues(A);
}
I have an spreadsheet like the following.
A B C
7- Test [red]
8- Test1 [yellow]
9- Test2 [red]
So, I'm trying to pass a loop in the column C, starting on C7. When it gets the color red, I should be able to get the values from column A, which are Test and Text2.
Here is the related code (it's not entering on the if)
SpreadsheetApp.getUi()
var s = SpreadsheetApp.getActiveSheet();
var dataCor = s.getRange('C7:C').getBackgrounds();
Browser.msgBox(dataCor); //Here I show the codes from the colors.
var dataValor = s.getRange('A7:A').getValues();
Browser.msgBox(dataValor); //Here I show the values from the column A.
var list = [];
var n = 0;
var n2 = 0;
for(var i = 0; i < dataCor.length; i++)
{
n = n + 1;
if(dataCor == '#cc0000')
{
list[n2] = s.getRange('A7'+n-1).getValue();
n2++;
}
}
Browser.msgBox(list);
PS: If you guys have any suggestions, please tell me.
PS1: Just to knowledge, is there any function that ignores the weekend days on a math? For example, if I get the date 22 of february, I know that 20 and 21 should be ignored so people would be able to work from 15 to 19. Anyway, it's not related to the topic.
I agreed to #serge-insas,
rather than going through two arrays for background and value, we can use same range. Just a thought
function getValueByColor()
{
var s = SpreadsheetApp.getActiveSheet();
var myRange = s.getRange('A7:C');
var list = [];
for (var i = 1; i <= myRange.getNumRows(); i++)
{
// 3 -column c :
if(myRange.getCell(i,3).getBackground() == '#cc0000')
{
//1- column A
list.push(myRange.getCell(i,1).getValue())
}
}
//debugger;
Browser.msgBox(list);
}
The arrays you get from your getValues() statements are 2D arrays, in other words, arrays of arrays.
In you Logger you should see something like that : [[xx],[yy],[zz],...]
so your comparison should compare the content of the array at the desired index and not - as you did - the entire 2D array.
In your script replace
if(dataCor == '#cc0000')
with
if(dataCor[n][0] == '#cc0000')
and similarly when you add the result to the output list :
list[n2] = dataValue[n]
which I would replace be using the array.push method
list.push(dataValue[n])
And in the end, when you get your list array write it in one single step to your sheet column using something like
s.getRange(1,1,list.length,list[0].length).setValues(list)
EDIT
Following iJay's answer which is using spreadsheet service calls in the loop (which is a bad idea because it is very slow - see best practices here- )
here is his code modified for a better efficiency ;
function getValueByColor(){
var s = SpreadsheetApp.getActiveSheet();
var myRangeValues = s.getRange('A7:C').getValues();
var myRangeColors = s.getRange('A7:C').getBackgrounds();
Logger.log(myRangeColors)
var list = [];
for (var i = 0; i < myRangeValues.length; i++) {
if(myRangeColors[i][2] == '#cc0000'){ // idx 2 is 3rd column = C
list.push([myRangeValues[i][0]]) // grab value in column A
}
}
Browser.msgBox(list);
}
Edit 2
To get col A and B in the result list simply change this line :
list.push(myRangeValues[i][0]+' | '+myRangeValues[i][1] ) // grab value in column A and B
note : since Browser.msgBox does not handle "new line" I'd suggest using HTML to show it the way you want.
Code :
...
var list = '';
for (var i = 0; i < myRangeValues.length; i++) {
if(myRangeColors[i][2] !== '#cc0000'){ // idx 2 is 3rd column = C
list+=myRangeValues[i][0]+' | '+myRangeValues[i][1] +'<br>' // grab value in column A and B
}
}
var result= HtmlService.createHtmlOutput(list)
SpreadsheetApp.getUi().showModalDialog(result, 'result')
}
How do you write a function/ listener in javascript that can fire when html updates?
html page is limited to the last 100 records
html page continuously adds new records (nodes)
I need to...
push the values into an array or increment a variable to sum the values
display the running total in html
Trying to create a counter that adds new values to the sum.
I believe the code below only executes once.
window.onload = function() {
var data = document.getElementsByTagName('span');
var len = data.length;
var total = 0;
var searchValue = "value";
for (var i = 0; i < len; ++i) {
var styles = data[i].getAttribute('style');
if (styles == searchValue) {
var txt = data[i].innerHTML;
split = txt.split(" ");
total += parseInt(split[2]);
}
}
console.log(total);
};
Yes you can do this with just JavaScript, and a little HTML/CSS for displaying the number.
Note that it requires a fair deal of experience to manage an addon with cross-origin requests, and to show it to the user.
I am trying to write a script in Google Apps Script that takes cell information from one sheet and copies it to another sheet, both for just grabbing certain columns to display on the second sheet and also a condition based on the values inside cells in a certain column. Here is what I have so far:
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("xxxxx");
var masterSheet = myMaster.setActiveSheet(myMaster.getSheets()[0]);
var myNames = SpreadsheetApp.openById("xxxxx");
var namesSheet = myNames.setActiveSheet(myNames.getSheets()[0]);
var row1 = masterSheet.getRange(1, 1, masterSheet.getLastRow(), 1);
var rowV = row1.getValues();
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for (var i = 1; i < rowV.length; i++) {
dataList.push(firstArray[i][0]);
dataList.push(firstArray[i][1]);
dataList.push(firstArray[i][2]);
dataList.push(firstArray[i][3]);
}
for (var j = 0; j < rowV.length - 1; j++) {
namesSheet.getRange(2, j + 1, 1, 1).setValue(dataList[j]);
}
}
So as of now it only works on one row, starting from the second row (to allow for column headers). And I suppose when I want to grab rows conditionally based on cell data, I will use an 'if' statement for the condition inside the 'for' loop, but I want the data to copy to the next available row in both sheets. I suppose I'd use something like:
' getLastRow + 1 '
or something like that. I need this code to be as efficient as possible because of the amount of data and its purpose. I am pretty new to programming so please explain in detail, and thanks again.
I'm not sure I understood exactly what you wanted to do but -from what I understood- this code snippet should give you a better way to start with...
(I added a few comments to explain in the code itself)
function onMyEdit() {
var myMaster = SpreadsheetApp.openById("MasterSheet ID");
var masterSheet = myMaster.getSheets()[0]; // get 1rst sheet
var myNames = SpreadsheetApp.openById("NamesSheet ID");
var namesSheet = myNames.getSheets()[0]; // get 1rst sheet
var firstArray = masterSheet.getDataRange().getValues();
var dataList = [];
for ( r = 1; r < firstArray.length; r++) { // iterate the first col of masterSheet
if(firstArray[r][0]=='some condition'){ // if value in the first column == 'some condition get the second column cell in the new array (here you could change what you want to get)
dataList.push([firstArray[r][1]])
}
}
Logger.log(dataList)
if(dataList.length>0){
namesSheet.getRange(1,namesSheet.getLastColumn()+1,dataList.length,1).setValues(dataList);//copy data in a column after last col
}
}