I'm looking to find a way to write a script that behaves similarly to a vLookup in google sheets.
Here is a link to a simplified example document.
What I am trying to do is use "Sheet1" as a form of sorts. When I enter information in "Sheet1" column B I want to be able to hit a custom menu button and have the information in "Sheet1" column B automatically populate correspondingly based of the values in column A into the first empty column on "Sheet 2".
I can write the script to create the custom menu and execute the function but I'm unsure of how to write the function itself.
Here is how such a function can look like. It gets pointers to each sheet, then the appropriate range from each: columns A and B, ignoring empty rows at the bottom. Then gets the values and begins comparing them: when columns A match, column B is assigned to. The final line puts the modified array values back into Sheet2.
function vl() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var sheet2 = ss.getSheetByName('Sheet2');
var range1 = sheet1.getRange(1, 1, sheet1.getLastRow(), 2);
var range2 = sheet2.getRange(1, 1, sheet2.getLastRow(), 2);
var values1 = range1.getValues();
var values2 = range2.getValues();
for (var i = 0; i < values1.length; i++) {
for (var j = 0; j < values2.length; j++) {
if (values1[i][0] === values2[i][0]) {
values2[i][1] = values1[i][0];
}
}
}
range2.setValues(values2);
}
Related
I want to be able to add all numbers in Column F - Male and Column G - Female to the Number of Passengers automatically using Google Apps Scripts.
Technically you don't need app scripts to automatically do have a dynamic column of ranges summed. You could put this formula in cell H2 and it will autopopulate... =Filter(F2:F+G2:G,F2:F<>"")
If you wanted to create an app script to apply this to all sheets (EDIT: only sheets whose name is in the array sheetNamesToChange) ... you could do this:
/** #OnlyCurrentDoc*/
function runMacro() {
const sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
sheets.forEach(addFormula_);
}
function addFormula_(aSingleSheet) {
const theFormulaToUse = '=Filter(F2:F+G2:G,F2:F<>"")';
const theAddressToUse = 'H2';
const sheetNamesToChange = [
'Sheet1',
'Sheet7',
'Sheet9'
];
//tests to see if aSingleSheet name is in the array sheetNamesToChange
if (sheetNamesToChange.includes(aSingleSheet.getSheetName())) {
//if yes, applies formula here
aSingleSheet.getRange(theAddressToUse).setFormula(theFormulaToUse);
};
}
There are multiple approaches you can use.
Asuming you want to sum F1+G1 and display on H1 and so on.
You can set the value of the H column to be the corresponding SUM formula, or you can set the value to the result of the sum and do that in your code. The latter being slightly easier and more readable.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
//the input would be the letter of the columns you want to aggregate
//start row dictates from which row you want to begin.
//adjacentColumn would be where the results are written.
function aggregateColumns(column1, column2, startRow, adjacentColumn){
let lastRow = sheet.getLastRow();
for(let i = startRow; i < lastRow; i++){
var value1 = sheet.getRange(`${column1}:${i}`).getValue();
var value2 = sheet.getRange(`${column2}:${i}`).getValue();
sheet.getRange(`${adjacentColumn}:${i}`).setValue(value1+value2);
}
}
If you're unable to specify the adjacent column before hand, then you'd have to either translate A1 notation to row and column numbers, or simply use row and column numbers from the start.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
//Column inputs should be the column number. Index starts at 1
//start row dictates from which row you want to begin.
function aggregateColumns(column1, column2, startRow){
let lastRow = sheet.getLastRow();
for(let i = startRow; i < lastRow; i++){
var value1 = sheet.getRange(i, column1).getValue();
var value2 = sheet.getRange(i, column2).getValue();
sheet.getRange(i, column2+1).setValue(value1+value2);
}
}
I recommend you to use template literals if you don't want to bother with row, column indexes and want to use A1 notation instead.
Those are great options. However, you can also test this method:
/** #OnlyCurrentDoc*/
function SumColumn() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var dataRange = ss.getDataRange().getValues();
var startRow = 1;
for (var i=1; i <dataRange.length; ++i){
var row = dataRange [i];
var column1 = row [5];
var column2 = row [6];
var sum = column1+column2 ;
ss.getRange(startRow+i,8).setValue(sum);
}
}
The numbers in row [] belong to the column indexing where Column A is 0.
In your example, you are using Column F, which corresponds to the number [5], and Column G to the number [6].
However, the indexing of getRange() changes, and that one starts from 1. So Column A is 1, and for your case Column G is the number 8.
Reference
Indexing using getRange
I stuck with this. Seems easy at first then I got lost!
What I am aiming for:
to insert a row(s) based on a cell value (Col C) -- insert row(s) below
after a new row(s) is inserted, copy the data from previous row + string value from previous row, removing that value as well from the previous row.
I am trying to use this formula but I got lost...
function addRows(){
var ss = SpreadsheetApp.getActive();
var sheet1 = ss.getSheets()[1];
var dataRange = sheet1.getDataRange();
var dataValues = dataRange.getValues();
for (var i = 0; i<dataValues.length; i++) {
for (var j= 0; j<dataValues.length; i++) {
/* If blank or 0 - zero, skip */
if (dataValues[i][3] == "" || dataValues[i][3]== 0) {continue;}
/* If value is >=1, insert new row(s) below the active row */
if (dataValues[i][3] >=1) {
sheet1.insertRowAfter(i);
sheet1.getRange(...) // copy the data from previous + string
}
}
}
}
In your shared Spreadsheet, you want to convert from the values of "A2:B3" to the values of "A6:B11".
In your shared image, you want to achieve as follows.
From
Data1 A0HD, B0DP
Data2 C12X, D0B1, E2C1, F6H1
To
Data1 A0HD
Data1 B0DP
Data2 C12X
Data2 D0B1
Data2 E2C1
Data2 F6H1
You want to achieve this using Google Apps Script.
I could understand like above. If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Sample script:
In order to test the script, please use the following script to your shared Spreadsheet. And run the script.
function myFunction() {
var sourceSheetName = "Sheet6";
var destinationSheetName = "Sheet6";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sourceSheetName);
// Retrieve source values.
var values = sheet.getRange("A2:B3").getValues();
// Convert values.
var result = values.reduce(function(ar, [a, b]) {
var temp = b.split(",");
for (var i = 0; i < temp.length; i++) {
ar.push([a, temp[i].trim()]);
}
return ar;
}, []);
// Put values.
var dstSheet = ss.getSheetByName(destinationSheetName);
dstSheet.getRange(dstSheet.getLastRow() + 1, 1, result.length, result[0].length).setValues(result);
}
In above script, when the script is run, the values of "A2:B3" from the "Sheet6" are retrieved and the converted values are put to the last row of "Sheet6".
If only input values are put to the source sheet, you can also use var values = sheet.getRange(2, 1, sheet.getLastRow() - 1, 2).getValues(); instead of var values = sheet.getRange("A2:B3").getValues();.
Note:
This is a simple sample script. So please modify this for your actual Spreadsheet.
Reference:
reduce()
I'm new to Google Apps script, so sorry if this is a too easy question.
I've searched in the existing questions but could not make this work.
I have this function :
SpreadsheetApp.getActiveSheet().getRange(5,1).activate(); that works fine.
But instead of (5,1) I would like to have a dynamic value for the row number.
I would like to return the row number of a specific text that I chose in a list in A1.
Here is what I do :
function GoTo () {
var sheet = SpreadsheetApp.getActiveSheet();
var c = "EQUIV($A$1,A2:A100,0)+1";
{ SpreadsheetApp.getActiveSheet().getRange(c,1).activate(); }
}
If I put = EQUIV($A$1,A2:A100,0)+1 in a cell, i do get the right number I'm looking for.
Thanks a lot !
Could you help me ?
If I understand what you want the following function looks in column A starting at A2 and searches for what you entered in A1. If found, it activates the cell of the found value. It also returns the row number of the found row in cell B1.
function findText() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var find = sheet.getRange("A1").getValue()// cell of value to find in column A
var values = sheet.getRange(2, 1, sheet.getLastRow()-1).getValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == find) {
row = i+2;
sheet.getRange(1, 2).setValue(row); //row number found to C2
var range=sheet.getRange("A"+row)
range.activate() //activate cell of found value
}
}
}}
You could run this with an onEdit trigger.
I am trying to create some basic conditional formatting rules with Scripts instead of the conditional formatting editor in sheets, because when a new row is added or removed, it breaks the rules already set. For example, when a row is deleted, the condition may be for the range A:C but then it adds "A1:C5,A6:C898". This causes some rows to be skipped by the rules so I am hoping a script will solve this.
So I want to simply change the cell background to green if the cell text is exactly "Y". I want to change it to red if text is exactly "N". I have other rules that I want to use but I am having trouble with the basics on this.
What I have so far in my script:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("PRECLOSING");
var range = sheet.getRange("E:E,I:J,M:M,P:P,S:V,AB:AC,AF:AF");
range.activate();
var values = rangeY.getValues();
//for each row that data is present
for(var i = 0; i < values.length; i++) {
var cell = sheet.getRange(i + 1, 2);
if ( values == "X" )
{
cell.setBackground('black');
return;
} else {
cell.setBackground('white');
}
}
}
I made some modifications to your code.
The function getRange i don't think it can accept the ranges in that way, instead i think it can get just ranges that are continue (eg. A1:E10)
For that I created an array with the ranges you need and then I loop through them.
The function "getValues" returns a two dimensional array so you will need a second loop to get the actual value from the cell.
Then as you already have the range, you need to get the cell inside that range and not a new complete range.
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("PRECLOSING");;
var columns = ['E:E','I:J','M:M','P:P','S:V','AB:AC','AF:AF'];
for(var col in columns){ //loop to iterate the desired ranges
var range = sheet.getRange(columns[col]);
range.activate();
var values = range.getValues();
//for each row that data is present
for(var i = 0; i < values.length; i++) { //loop to iterate the rows
for( var j = 0; j< values[i].length ; j++){ //loop to iterate the columns
var cell = range.getCell(i+1, j+1);
if ( values[i][j] == "X" )
{
cell.setBackground('black');
}
}
}
}
}
Hope this helps.
I have two sheets on a Google Spreadsheet. One has a lot of information and references and the other has the same reference in the first cell of a column with link names and links below. I am trying to get around the "no multi-hyperlinking in one cell" limitation by having the user input the reference they want to search and then searching through the second sheet to find the reference and have a pop-up box with the links.
So far, I am able to get the links from the second spreadsheet column and display them in a pop-up box with this code:
function main(){
var column = SearchAndFind()
showURL(getLinks(column))
}
function getLinks(col){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.setActiveSheet(ss.getSheets()[1]);
var cell = ss.getActiveCell();
var values = sh.getDataRange().getValues();
var myArray = []
for(n=1;n<values.length;++n){
var cell = values[n][col] ;
myArray.push(cell);
}
return myArray;
}
function showURL(data){
var app = UiApp.createApplication().setHeight(40+8*data.length).setWidth(200);
app.setTitle("Show URLs");
var panel = app.createVerticalPanel();
app.add(panel);
for(var d=0 ; d<data.length;d=d+2){
var link = app.createAnchor(data[d],data[(d+1)]);
panel.add(link);
}
var doc = SpreadsheetApp.getActiveSpreadsheet();
doc.show(app);
return;
}
When I hard-coded a random column number to the getLinks function and it worked fine but I need to be able to get the column number from a user search of the first cell in each column in the second sheet.
This is the code I have right now that doesn't work:
//I know that it will always be the second sheet on the spreadsheet
//Search the column headers on the second sheet
//When one matches, return the index
function SearchAndFind(){
//Make the 2nd sheet active
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.setActiveSheet(ss.getSheets()[1]);
var range = sh.getRange(1, 1, sh.getMaxRows(), 1);
var values = range.getValues();
//Get the user input for the text they want to search
var ui = SpreadsheetApp.getUi();
var search = ui.prompt('Enter the ID: ');
var searchString = search.getResponseText()
//for loop to iterate through the first row and find the matching cell
//return the index of that column
for (n = 0; n < values[0].length; n++){
var cell = values[0][n]
if (cell === searchString){
return n;
}
}
}
When I run all of the code (including the function SearchAndFind that doesn't work), the pop-up box comes up with undefined, linking nowhere. Admittedly, I don't have a lot of experience with Javascript so I think I just don't understand it well enough to find the bug here.
You are pulling only one column and then checking for the match in columns.
var range = sh.getRange(1, 1, sh.getMaxRows(), 1);
Gives you only the first column. And
for (n = 0; n < values[0].length; n++){
Looks through those columns. So your values[0].length is 1, and your loop only runs once.
Are you trying to loop through all rows form first column, or through first row of all columns.
Also you should change your loop to
for (var n = 0; n < values[0].length; n++){