How to copy row ranges in Google docs? - google-apps-script

I have a Spreadsheet, like excel on Google Docs. I am using both Mozilla Firefox and Google Chrome, whichever works. Almost all my columns have dropdown list validation(you know, each cell has a dropdown list to select, I hope I made it clear). I arranged them when I first created the spreadsheet, gave all the columns validation from ranges I created.
My problem is, whenever I add a new row, that row doesn't have any validations, all of them are gone. The old rows still have the validations.
So then, I set the validations every time I add a new row, one by one. This is frustrating. Some people also had the same problem, asked online, but no one answered.
When I copy an empty row with validations and paste it on the new row, it works fine. So, what I am saying is, can you help me write a script for it? Like copying 5 rows when I execute the script?
I am trying to study the scripts but I did nothing nothing so far. I think
var actSc = SpreadsheetApp.getActiveSpreadsheet();
var range = actSc.getRange("A1:B1");
This all I got from the examples I saw. I mean it. I got nothing.
If this copies the ranges of one cell, then I guess I should do it for all my columns.
But how do I put them in the new row? Is there something like setRange?
I could really use some help. This is driving me crazy and I really don't get this script thing.
What I mean by range is that I have ranges like "STATES" and it includes "NY,LA,CA" etc. This NY,LA,CA fills the dropdown list in the cells of that STATES column. I hope this getRange means this range.
Sorry about my English.

If I understand correctly, you want to script a function that will add new rows to a sheet and maintain the existing validations for your columns. This is certainly possible and not too difficult. One approach could be a "refresh validations" function that updates your entire sheet all at once, in the event that you want to reuse it in other sheets. First, though, it sounds like you could use a brief overview of the object classes you need to know about to do basic Google Apps Scripts:
SpreadsheetApp - Think of this class as the foundation of the Spreadsheet Service. It provides file I/O and functionality that is not tied to specific spreadsheets, per se, such as UI and the creation of Data Validation sets. It's the interface to all of your individual spreadsheet documents.
Spreadsheet - A spreadsheet document file, which can contain multiple Sheets. This is what gets created when you create a new Google Sheets document in Drive. Provides document-level functions, such as the ability to manage ownership, set permissions, access metadata, etc. There's some overlap with the Sheet class, so this one can seem like a bit of a mishmash.
Sheet - An individual sheet is what you normally think of as a spreadsheet: a set of rows and columns. Each Spreadsheet document can contain many, distinct Sheets. The Sheet class lets you modify the overall appearance of the sheet. You can freeze or hide rows, protect ranges of cells from being edited, add/delete rows and columns, etc. You can also get data about the sheet, such as the last row that has content or the maximum range of the whole sheet.
Range - Dropping down another level, we reach the Range object, which represents a certain rectangular area of cells. This can be as small as a single cell or as large as the whole sheet. It does not seem possible, however, for Ranges to represent discontiguous cells. This is where you had some trouble, because you treated the Range object as content that you could copy and paste in your sheet, which is understandable. But a Range isn't the data in the cells it represents. It's just an interface to those cells. If you want to access the data itself, you have to drop down to the bottom level of the hierarchy:
Value - The actual contents of your sheets are normal JavaScript values: strings, integers, Booleans, etc. that you can manipulate with the subset of JavaScript that Google Apps Script supports.
In order to do something with the values in your sheet, you first get the Range object from the Sheet (which you get from the SpreadsheetApp) and then get the values from the Range:
var values = SpreadsheetApp.getActiveSheet().getRange("A1:B1").getValues(); // returns [[]]
Note that getValues() returns a multi-dimensional array. As a representation of the values in your sheet, it looks like this:
// row 1 [[column A, column B, column C, column D, ...],
// row 2 [column A, column B, column C, column D, ...],
// row 3 [column A, column B, column C, column D, ...],
// row 4 [column A, column B, column C, column D, ...],
// row 5 [column A, column B, column C, column D, ...], ...]
So if the range A1:B1 is a range of one row and two columns, you can retrieve the values with A1 notation or by specifying the upper left row and column of the range, and the number of rows and number of columns you want to retrieve:
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A1:B1");
var range = sheet.getRange(1, 1, 1, 2); // row 1, column 1, 1 row, 2 columns
var values = range.getValues(); // returns [[50, 100]]
If the value in A1 is 50, and the value in B1 is 100, the last function above will return [[50, 100]]. You can access individual cell values directly, too:
var range = sheet.getRange("A1");
var value = range.getValue(); // returns 50
var cell = range.getCell().getValues(); // returns [[50]]
Obviously, you can set the values of ranges, too:
var range = sheet.getRange("A1:B2");
range.setValues([[50, 100]]);
range = sheet.getRange(1, 1); // same as sheet.getCell(1, 1)
range.setValue(50); // the value of A1, or row 1 column 1, is now 50
The next step is to figure out how the Data Validation class works. You create a Data Validation object using the Data Validation Builder, which lets you chain together a series of rules to apply to a range. You then set the range to that Data Validation rule set:
var stateList = ["AK", "AL", "AR", ...];
var rules = SpreadsheetApp.newDataValidation() // create a new Data Validation Builder object and use method chaining to add rules to it
.requireValueInList(stateList, true) // first param is the list of values to require, second is true if you want to display a drop down menu, false otherwise
.setAllowInvalid(false) // true if other values are allowed, false otherwise
.setHelpText("Enter a state") // help text when user hovers over the cell
.build();
range.setDataValidation(rules); // apply the rules to a range
Now you can insert rows and the rules should copy over into them automatically:
var lastRow = sheet.getLastRow(); // get the last row that contains any content
sheet.insertRowAfter(lastRow);
Or copy the rules and use them elsewhere:
var cell = sheet.getRange(1, 1, 1, 1);
var rule = sheet.getDataValidation(); // returns rule
var range = sheet.getRange("A1:B1");
var rules = range.getDataValidations(); // returns [[rules, rules]]
var lastRow = sheet.getLastRow(); // or sheet.getMaxRows()
range.setDataValidations(rules);
So you can very easily put these concepts together to write whatever sort of function you need to add rows, build validation rule sets, and add validations to new ranges of cells. You can do most of these things more concisely than I have here, but it sounds like you're looking for a more in-depth explanation. I hope it helps.

var sheetToUpdate = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheetToUpdate.insertRowAfter(sheetToUpdate.getLastRow());
var rangeToUpdate = sheetToUpdate.getRange(sheetToUpdate.getLastRow()+1,1,1,sheetToUpdate.getMaxColumns());
sheetToUpdate.getRange(sheetToUpdate.getLastRow(),1,1,sheetToUpdate.getMaxColumns()).copyTo(rangeToUpdate, {formatOnly:true});

Related

Google Sheets looking for a google script like VLOOKUP but combines the found values and joins them into a specific cell

I have a spreadsheet where I'm trying to add an automatic search function whenever a cell on the Sheet "List" Column 1 gets selected.
It's sort of like using a VLOOKUP function of the sheets but I've been unsuccessful trying to come up with an effective code for it.
Test Spreadsheet link
I made a simple test spreadsheet hopefully for people to easily understand how the sheet should work.
In the 'Database' sheet, there's a list of people with the fruits they like or dislike.
In the 'List' sheet, the list is reversed where the fruits are now the main list but here, when a person clicks on the cells with the fruits, the top cells should automatically update with the combined names of people who like or dislike that certain fruit.
The function should go like this:
If I click a cell on column 1 of the 'List' sheet,
A1 cell updates it's value with the active cell value
B1 cell updates it's value with the combined values based on who likes it from the 'Database' sheet Column 2.
B2 cell updates it's value similar as above but from Dislikes in Column 3
I've attached the code I currently have but I'm not able to get the last 2 steps working.
There is also a guide in the sheets on what the correct answers should look like for each fruit.
I got stuck looking for a way to make the steps 2 and 3 work. I was able to find a code here but it stops after finding one match. stackoverflow.com/questions/10838294/… My current problem is: Since the cells [B2:B] to filter from on the Database sheet has multiple values, is it possible to find all cells with at least a partial match, get the values of the cells on the left of that then list them into the B1 cell on the "List" sheet?
Test Spreadsheet link
Code.gs
function onSelectionChange(e){
var currentsheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var currentcell = currentsheet.getActiveCell();
var activerow = currentcell.getRow();
var activecol = currentcell.getColumn();
var sname = currentsheet.getName();
// Check if current sheet name is correct
if (
sname == 'List'
)
{
// Check if this on first column
if (activecol == 1)
{
// ACTION
var name = currentcell.getValue(); //Get current cell for searching
var prefsheet = SpreadsheetApp.getActive().getSheetByName("Database");
var last = prefsheet.getLastRow();
var data = prefsheet.getRange(1,1,last,2).getValues(); // create an array of data from columns A and B
for(nn=0;nn<data.length;++nn){
if (data[nn][1]==name){break} ;
SpreadsheetApp.getActiveSheet().getRange('A1').setValue('Fruit: ' + name);
SpreadsheetApp.getActiveSheet().getRange('B1').setValue('Like: ' + data[nn][0]);
}
}
};
};
I was able to find a workaround for this and it turns out I didn't have to use a Google Script code to keep updating the formula on List!B1.
I'm new with scripting and sheets but I'll be sharing this formula here in case someone needs something like this in the future.
This is what I used on List!B1
=JOIN(", ",query(filter(Database!A2:B,REGEXMATCH(Database!B2:B,A1)),"Select Col1"))
Regexmatch does the search for partial value and returns as "TRUE"
Filter will then list these rows and query "Select Col1" will only keep the first column of the results and remove the second column. Lastly, Join formula will concatenate them into one cell and add separators.
Which is working very nicely for my purpose. If anyone has any suggestions I'd love to hear about it too.

Trying to copy a variable-length list

I'm trying to copy a list from Sheet A to sheet B. (I have the number of rows used by the data in the list saved in a cell, if needed.)
The spreadsheet file contains 5 sheets, of which "Analysis" is one. It contains 4-5 tables, each separated by a blank row.
The list itself is variable in length - sometimes it's 9 rows, sometimes its 17. Thankfully, it's always 5 columns wide.
Once I get the list into Sheet B, I need to set all cell borders to TRUE.
Here's the code that I've been playing with:
var sss =
SpreadsheetApp.openById(Sheet A);
var ss = sss.getSheetByName('Analysis'); //
var tblRows = sss.getRange('Analysis!K190').getValue();
var range = SpreadsheetApp.getRange('190,5,tblRows,5'); //assign the range you want to copy
var data = range.getValues();
var tss =
SpreadsheetApp.openById(Sheet B);
var ts = tss.getSheetByName('2018');
ts.getRange(ts.getLastRow()+1,1,tblrows,5).setValues(data);
(I'm not sure that this formatted properly)
Anyhow, I have the number of rows used by the list saved as a variable in a spreadsheet cell, and I'd like to use that, because I have found "getLastRow" to get the last row of the sheet, not the last row that is non-blank. (It might be giving me trouble because I'm doing something wrong, tho...) I know that I've messed up, so feel free to throw everything out and start over, if needed.
Anybody care to throw some ideas my way?

compare two colums in different spreadsheets in google script

i want to compare tow different columns in two different spreadsheets.
My first spreadsheet is named "testtabelle" and the other is named "test" the name of the sheets are both "Tabellenblatt1"
I want to compare column A # testtabelle with column A # test.
If the string are equal, i need the value from colum B # test and copy it into column b # testtabelle of the same row, where my strings matched.
I think i need two loops for every column and a if statement to compare the values.
I'll be glad if someone can help me!
You can use the SpreadsheetApp class to open multiple sheets. Look up openById or openByUrl, either should work. You can then make two spreadsheet objects, get the values of column A and B of each, iterate through to compare, and copy the value of column B if the values of column A match.
One thing to note is you should use getValue and setValue rather than copyTo as I don't think the latter works across separate spreadsheets.
You should end up with something like this:
// gets spreadsheet A and the range of data
ssA = SpreadsheetApp.openById('ID of spreadsheet A');
sheetA = ssA.getSheetByName('name of sheet in ssA');
dataA = sheetA.getRange('A:B').getValues();
// gets spreadsheet B and the range of data
ssB = SpreadsheetApp.openById('ID of spreadsheet B');
sheetB = ssB.getSheetByName('name of sheet in ssB');
dataB = sheetB.getRange('A:B').getValues();
// loops through column A of spreadsheet A & B and compares
for(var i = 0; i > sheetA.getLastRow(); i++){
// checks to see if ith value in 2nd row is the same
if dataA[1][i] == dataB[1][i]{
var value = sheetA.getRange(i+1, 2).getValue();
// used i+1 because index of range is 1, while index of the data array is 0
sheetB.getRange(i+1, 2).setValue(value);
} // end if
} // end i
There's also a similar question answered here.

google sheets form function not updating with data input from the script

I'm having some trouble getting a function in a google sheet to calculate using values inserted by a script.
I have a google script that is adding data to a sheet based on user-inputted data from a form that the script has created. So, in the form, a user inputs their name, selects a product and some options, and the script adds this to a sheet named 'Client Data Sheet'.
I then have a different sheet which is supposed to do the math to calculate the price. My script copies all the functions from a hidden template row into the next-available row, so, for example, cell D7 contains ='Client Data Sheet'!A4, D8 contains ='Client Data Sheet'!B4 etc... These all display the correct values.
The problem is the price calculation function, in that same calculation sheet, which has a rather complex function that a previous coder wrote. This function should calculate the price of the product and options based on the data in the same sheet. It does so without calling a script, just pulling data from cells in other sheets, running some if() checks to decide whether or not to add extra costs to the total price, and adding it all up.
Problem is, it doesn't update based on the new data. It just shows 0, as though the other cells were empty, even though they now contain data updated by pulling data from another sheet which was edited by a script. If I go in to edit the function and just press enter, it re-calculates correctly, so I basically just need the function to re-evaluate based on the new data that is in the cells it's dependent on.
My theory is that it's not updating the function since I didn't directly edit the cells it's dependent on. I could try to change the awkward, huge function this other coder wrote so it pulls from the spreadsheet my script edits, rather than from cells that copy in that data, but that seems like a workaround, and is unsatisfying.
TL;DR: a function isn't updating based on data that changes in cells that copy the data from other cells which are filled by a script. Anybody have any advice for how to get the function to update?
EDIT: Ok, so if I make sure that the function that isn't updating pulls at least some data from cells that the script updates, it works. It seems that it doesn't recognize that the cell once removed has updated as well. It would be better, though if it was able to pull from the cells that pull their data from the cells the script updates. This would let other users of the sheet change the products if a customer requested a change later on without having to edit my hidden sheets that the data is pulled from.
Code:
This copies a template row in my spreadsheet that contains the math functions:
function new_client(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Liquidation');
if(ss.getName()=="Data" || ss.getName().indexOf("Liq")==0){
var last_row = ss.getLastRow();
var last_col = ss.getLastColumn();
ss.insertRowsAfter(last_row, 1);
var template_row = 4;
var copy_range = ss.getRange(template_row, 1, 1, last_col);
var paste_range = ss.getRange(last_row+1, 1, 1, last_col);
copy_range.copyTo(paste_range, {contentsOnly:false});
paste_range.getCell(1,1).setValue("NO");
SpreadsheetApp.flush();
}
}
This copies data from the form into a different spreadsheet, which the sheet edited above pulls data from:
var clientSheet = SpreadsheetApp.getActive().getSheetByName('Client Data Sheet');
clientSheet.insertRowsAfter(clientSheet.getMaxRows(), 1)
var lastRow = clientSheet.getLastRow() + 1;
var lastColumn = clientSheet.getLastColumn();
var destRow = clientSheet.getRange(lastRow, 1, 1, lastColumn);
var column = 1;
for (var key in user) {
if (user.hasOwnProperty(key)) {
destRow.getCell(1, column).setValue(user[key]);
column++;
}
}
So, for example, after this code is run, the Client Data Sheet contains a cell, B4, which now contains the name of the chosen product, "foo". The Liquidation sheet has a cell let's call it A5 that contains the function ='Client Data Sheet'!B4, as well as another cell which has the price calculation function: =if(A5="foo", 100, 0)
When the script above inserts the values from the form, the cell B4 in Client Sheet and the cell A5 in the Liquidation sheet will contain the right value, but the cell with the calculation function =if(B4="foo", 100, 0) will not update.

Google Apps Script to VMerge tables WITH FORMATTING

Does anyone know if there is a Google apps script out there that does what VMerge does but keeps the formatting of the tables being merged together? (in Google Spreadsheets)
VMerge is a script that can be used as a custom formula but a script that I can trigger myself will do just fine too.
Any help would be much appreciated.
VMerge expects arrays-of-values as parameters, and therefore does not know what cells were referenced creating those arrays. When used as a Custom Formula, the sheet parser resolves all range parameters into their values before passing them to VMerge. Additionally, the parameters may be hard-coded or be the result of Queries or other functions that return ranges. Because of this alone, it's not feasible to modify VMerge to copy cell formats to the new merged table.
Complicating things further, Custom Functions cannot modify cells outside of the one they are attached to, they can only return values or arrays of values. From comment in Issue 37:
2) Scripts used as cell functions are not allowed to do complex things
like connect to other APIs or set the values of other cells. Scripts
used as cell functions are only allowed to return a value.
So you're going to have to settle for a function you call from scripts. The following function will join multiple ranges into a new table at a given anchor point. Because I started out trying to make this a custom function callable from a sheet, the parameters are string expressions of ranges, in a1Notation. (It could easily be refactored to deal directly with Range objects.)
The "Anchor" for the new range is expected to be a cell. One or more ranges of any size may be joined - each will be positioned directly below the previous.
Examples:
VJoin("D1","A1:B"); - All of columns A & B duplicated in columns D & E
VJoin("Sheet2!A1","Sheet1!C9:E10","Sheet1!A14:B15"); - Two different ranges in Sheet 1 joined and copied to Sheet 2.
Here's the code:
/*
* Vertically join the ranges from multiple sources into a new table
* starting at the given anchor point. Values and formatting are copied.
*
* #param {a1Notation} anchorA1 Anchor for joined table.
* #param {a1Notation} sources One or more source ranges.
*/
function VJoin(anchorA1,sources) {
var sheet = SpreadsheetApp.getActiveSheet();
var anchor = sheet.getRange(anchorA1);
var anchorSheet = anchor.getSheet(); // in case anchorA1 is not on the "active sheet"
var nextAnchor = anchor;
for (var i in arguments) {
// Arguments are expected to be Strings, containing a1Notation.
if (i == 0) continue; // First argument was anchorA1, skip it.
if (arguments[i].constructor == String) {
var source = sheet.getRange(arguments[i]);
var destination = anchorSheet.getRange(nextAnchor.getRow(), nextAnchor.getColumn(),
source.getNumRows(), source.getNumColumns() );
// Copy all values & formatting to new location.
source.copyTo(destination);
// Prepare for next range by moving our anchor
nextAnchor = sheet.getRange(nextAnchor.getRow() + source.getNumRows(),
nextAnchor.getColumn());
}
else {
throw new Error ("Expected String containing a1Notation.")
}
}
}
If you need a separate script to bring over the formatting...
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('Sheet1');
s.getRange('A1').copyFormatToRange(sheet, column, columnEnd, row, rowEnd);
}
I find the below built in functions to work well pulling information from different Google Sheet files. I have defined named ranges to define what columns to pull into the Master, and also know I am having an issue with Feb.
=sort(arrayformula({
importrange("1sTS3AUfoXqXYrMYJrro9pGEKwqVL_k854yhniNOHNWc","JCJan");
importrange("1ETSD4J-8AI-7pVK0hXJKaWtG3RlHKpnco88Yj8sqNN8","JCFeb")}),1,True)