What is the closest function to INDEX( ,MATCH( in Google Apps? - excel

I have a master spreadsheet which has the excel function:
=INDEX(D2:D4,MATCH(F11,B2:B4,0))
How would you express this in Google Apps or is there anything close to it?
Here is the part of the code which needs to be modified:
var link = masterSheet.getRange('F11').getValues();//here is the problem
//=INDEX(D2:D4,MATCH(F11,B2:B4,0))
var ss = SpreadsheetApp.openById(link);

If you are wanting to achieve the same functionality in Google Apps Script, and if I'm understanding correctly:
var lookup = masterSheet.getRange('F11').getValue();
var range = masterSheet.getRange('B2:D4').getValues();
var lookupRange = [];
for (var i = 0; i < range.length; i++) lookupRange.push(range[i][0]);
var index = lookupRange.indexOf(lookup);
if (index == -1) {
//error, not found
}
else {
var link = range[index][2];
var ss = SpreadsheetApp.openById(link);
//etc
}
Caveat: if you are searching for dates and/or times, this will fail. You will need to convert the date objects into something else that can be found with indexOf. Post back if this is the case.

That same formula should work equally well in both Excel and google spreadsheets, doesn't it work? VLOOKUP could also be used in both to do the same, i.e.
=vlookup(F11,B2:D4,3,0)

Related

Is there a google-script method like Application.Intersect(Target, Range) in Excel?

Now I understand that the question is deeper, and is connected with the tracking of events.
In Excel, I use this code:
If Not Intersect(Target, Sh.Range("$A$1:$A$300")) Is Nothing sub_do_something()
Here, Target - the address of the selected cell, Intersect determines whether the cell belongs to the specified range.
I use it in the system for filling and calculating the costing of the project.
The user clicks a row in a specific section of the calculation template. The script determines the address of the selected cell and switches the user to a specific sheet of the directory. Next, the user clicks on the desired line of the directory, the script copies a certain range of cells in the line and returns the user back to the calculation. When this happens, the copied data is inserted into a range of cells, starting with the selected one.
Thus, the creating a calculation, in which there can be more than 100 positions, is greatly simplified.
In Excel, everything works fine, but soon I plan to transfer this project to a cloud-based service, and Google Sheets is the best option.
Alas, only some events can be tracked in GAS, for example, using onOpen or onEdit triggers.
Excel has much more tracked events.
After a search on the StackOverflow, I found several similar issues related to tracing events, for example, How to find where user's cursor is in Document-bound script, Can we implement some code that fires upon selecting something in google document?, Google app script monitor spreadsheet selected ranges.
From the answers to these questions, it is clear that in GAS there is no such simple solution as Intersect(Target, Range) in Excel.
The last example uses the side menu, running a script from it that queries the sheet 5 times per second, and displays the address of the active cell in the "data" field.
Unfortunately, this code does not work for me. In the debugger, the getActiveRange() function works fine, but this code does not work:
$(document).ready(() => {
setInterval(()=>{
google.script.run.withSuccessHandler(log).getActiveRange();
},200)
})
log(e) => {
$('#data').val(e)
}
Question.
If someone did something similar, please share your experience.
Or tell me why this example does not work. If he can be reanimated, I will adapt him to solve my task.
I worked on a similar project and here's the solution:
function onSelectionChange(e)
{
var ss = e.source;
var Sh = ss.getActiveSheet();
var range = Sh.getRange("A1:A300");
var target = e.source.getActiveRange();
//check for intersection
if(RangeIntersects(target, range))
{
Logger.log("Changed Row: " + target.getRow() + "\nValue: " + target.getValue());
}
}
//returns true if target intersects with the predefined range
function RangeIntersects(target, range)
{
return (target.getLastRow() >= range.getRow()) && (range.getLastRow() >= target.getRow()) && (target.getLastColumn() >= range.getColumn()) && (range.getLastColumn() >= target.getColumn());
}
Here's an idea. I can't quite get it to work though.
Maybe someone else can give a better answer.
Also, having functions running 24/7 is not possible with GAS, I think, as there are limits to the total run-time. You may wish to add a code-guard that exits the script if the last update time is longer than 10 minutes ago or something.
function checkSelection() {
var spreadsheet = SpreadsheetApp.getActive();
var targetRange = spreadsheet.getRange('activate');
// Change your named ranged name here
var tCol = targetRange.getColumn();
var tLastCol = targetRange.getLastColumn();
var tRow = targetRange.getRow();
var tLastRow = targetRange.getLastRow();
var num = 0;
for (num; num < 115; ++num) {
// Repeats the code below 100 times
var range = spreadsheet.getActiveRange();
var row = range.getRow();
var col = range.getColumn();
if (col >= tCol && col <= tLastCol && row >= tRow && row <= tLastRow) {
range.setBackground('#000000');
// Change the code in this block to your code.
}
SpreadsheetApp.flush();
Utilities.sleep(500);
// Waits half a second before repeating
}
}
115 repetitions * 500ms wait seems to run for almost a minute, then the trigger will fire the whole function again.
Intersection of two Ranges
You can use this to calculate intersection of two ranges. It requires an object in the form of: {rg1:'A1Notation String',rg2:'A1Notation String'}
function calculateIntersection1(rgObj) {
var iObj={};
var ss=SpreadsheetApp.getActive();
var sh=ss.getActiveSheet();
var rg1=sh.getRange(rgObj.rg1);
var rg2=sh.getRange(rgObj.rg2);
var iObj={rg1colst:rg1.getColumn(),rg1colen:rg1.getColumn()+rg1.getWidth()-1,rg1rowst:rg1.getRow(),rg1rowen:rg1.getRow()+rg1.getHeight()-1,rg2colst:rg2.getColumn(),rg2colen:rg2.getColumn()+rg2.getWidth()-1,rg2rowst:rg2.getRow(),rg2rowen:rg2.getRow()+rg2.getHeight()-1};
if(iObj.rg1colst>iObj.rg2colen || iObj.rg1colen<iObj.rg2colst || iObj.rg1rowst>iObj.rg2rowen || iObj.rg1rowen<iObj.rg2rowst || iObj.rg2colst>iObj.rg1colen || iObj.rg2colen<iObj.rg1colst || iObj.rg2rowst>iObj.rg1rowen || iObj.rg2rowen<iObj.rg1rowst) {
return '<h1>No intersecting cells</h1>';
}else{
var vA1=rg1.getValues();
var v1=[];
var vA2=rg2.getValues();
var v2=[];
for(var i=0;i<vA1.length;i++){
for(var j=0;j<vA1[i].length;j++){
var s=Utilities.formatString('(%s,%s)', iObj.rg1rowst+i,iObj.rg1colst+j);
v1.push(s);
}
}
for(var i=0;i<vA2.length;i++){
for(var j=0;j<vA2[i].length;j++){
var s=Utilities.formatString('(%s,%s)', iObj.rg2rowst+i,iObj.rg2colst+j);
v2.push(s);
}
}
var oA=[];
for(var i=0;i<v1.length;i++){
var idx=v2.indexOf(v1[i]);
if(idx>-1){
oA.push(v2[idx]);
}
}
return Utilities.formatString('Intersecting Cells: %s', oA.join(', '));
}
}
It either returns the string "No Intersecting Cells" or a string identifying the intersecting cells in (row, column) format.

Find value on any sheet in spreadsheets using Google Script

Using the code below I'm able to look through multiple sheets in a spreadsheet to find the first value that equals the selected cell. The only problem with this bit is: The cell with the value found is highlighted yellow, but the cell with the value found isn't selected. See code below for hopping through sheets. I can't get my head around this :)
Funny thing is that the code for highlighting and selecting a value does work when I'm not hopping through the list of sheets, see the best answer: Find value in spreadsheet using google script
function SearchAndFind() {
//determine value of selected cell
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
var value = cell.getValue();
//create array with sheets in active spreadsheet
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
//loop through sheets to look for value
for (var i in sheets) {
//Set active cell to A1 on each sheet to start looking from there
SpreadsheetApp.setActiveSheet(sheets[i])
var sheet = sh.getActiveSheet();
var range = sheet.getRange("A1");
sheet.setActiveRange(range);
//set variables to loop through data on each sheet
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = sheets[i].getDataRange().getValues()
var step = 0
//loop through data on the sheet
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log(step+' -- '+value+' = '+data[r][c]);
if(data[r][c]==''||step==1){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
sheet.getRange(r+1,c+1).activate().setBackground('#ffff55');
return;
}
}
}
}
}
This code is able to search across multiple sheets, it is obviously based on your published code but uses a memory (scriptProperties) to keep the search value 'alive' when changing from one sheet to the next one and to know when to search for it.
It has 2 non-optimal aspects : 1° you have to keep searching up to the last occurrence before you can begin a new search.
2° : when it switches from sheet n to sheet n+1 it first selects cell A1 before finding the value occurrence.
I guess it should be possible to get rid of these issues but right now I don't find how :-)
Maybe the approach is simply not the best, I started from a simple one sheet script modified and complexified... that's usually not the best development strategy (I know), but anyway, it was a funny experiment and a good logic exercise ...
Thanks for that.
function SearchAndFind() {
//determine value of selected cell
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
var value = cell.getValue();
if(ScriptProperties.getProperty('valueToFind')!=''){value = ScriptProperties.getProperty('valueToFind')};
//create array with sheets in active spreadsheet
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets()
var sheetNumber = sheets.length;
var currentSheet = ss.getIndex()-1;
Logger.log(currentSheet);
//loop through sheets to look for value
for (var i = currentSheet ; i<sheetNumber ; ++i ){
Logger.log('currentSheet = '+i)
//Set active cell to A1 on each sheet to start looking from there
SpreadsheetApp.setActiveSheet(sheets[i])
// sheets[i].getRange(1,1).activate();
//set variables to loop through data on each sheet
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = sheets[i].getDataRange().getValues()
var step = 0;
//loop through data on sheet
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log('sheet : '+i+' step:'+step+' value '+value+' = '+data[r][c]);
if(data[r][c]==''||(step==1&&i==currentSheet)){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
sheets[i].getRange(r+1,c+1).activate().setBackground('#ffff55');
ScriptProperties.setProperty('valueToFind',value);
return;
}
}
}
cell = sheets[i].getRange(1,1);
}
ScriptProperties.setProperty('valueToFind','');
Logger.log('reset');
}

Find value in spreadsheet using google script

Situation:
1 spreadsheet
multiple sheets
1 cell selected (may vary)
What I'd like to do is to find and set focus to the next cell in any sheet that matches the selected cell (case insensitive) upon clicking a button-like image in the spreadsheet. Sort of like a custom index MS Word can create for you.
My approach is:
- set value of the selected cell as the variable (succeeded)
- find the first cell that matches that variable (not the selected cell) (no success)
- set value of found cell as variable2 (no success)
- set the focus of spreadsheet to variable2 (no success)
function FindSetFocus()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var activecell = sheet.getActiveCell();
var valueactivecell = activecell.getValue();
//here comes the code :)
}
I have found this snippet in the following topic, but I'm having a little trouble setting the input and doing something with the output: How do I search Google Spreadsheets?
I think I can replace 'value' with 'valueactivecell', but I don't know how to set the range to search through all sheets in the spreadsheet. Also, I'd like the output to be something I can set focus to using something like 'ss.setActiveSheet(sheet).setActiveSelection("D5");'
/**
* Finds a value within a given range.
* #param value The value to find.
* #param range The range to search in.
* #return A range pointing to the first cell containing the value,
* or null if not found.
*/
function find(value, range) {
var data = range.getValues();
for (var i = 0; i < data.length; i++) {
for (var j = 0; j < data[i].length; j++) {
if (data[i][j] == value) {
return range.getCell(i + 1, j + 1);
}
}
}
return null;
}
also found this but no luck on getting it to work on the selected cell and setting focus: How do I search for and find the coordinates of a row in Google Spreadsheets best answer, first code.
Please bear in mind that I'm not a pro coder :) If code samples are provided, please comment inline hehe.
Thanks in advance for any help.
Edit 24/10: Used the code from the answer below and edited it a bit. Now able to look through multiple sheets in a spreadsheet to find the value. The only problem with this bit is: My cell is highlighted yellow, but the cell with the value found isn't selected. See code below for hopping through sheets. I can't get my head around this :)
function SearchAndFind() {
//determine value of selected cell
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
var value = cell.getValue();
//create array with sheets in active spreadsheet
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
//loop through sheets to look for value
for (var i in sheets) {
//Set active cell to A1 on each sheet to start looking from there
SpreadsheetApp.setActiveSheet(sheets[i])
var sheet = sh.getActiveSheet();
var range = sheet.getRange("A1");
sheet.setActiveRange(range);
//set variables to loop through data on each sheet
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var data = sheets[i].getDataRange().getValues()
var step = 0
//loop through data on the sheet
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log(step+' -- '+value+' = '+data[r][c]);
if(data[r][c]==''||step==1){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
sheet.getRange(r+1,c+1).activate().setBackground('#ffff55');
return;
}
}
}
}
}
Here is an example of such a function, I inserted a drawing in my spreadsheet representing a button which I assigned the script so it's easy to call.
I added a feature to set a light yellow background on the resulting selected cell so it's easier to see the selected cell but this is optional.
Code
function findAndSelect(){
var sh = SpreadsheetApp.getActiveSpreadsheet();
var ss = sh.getActiveSheet();
var cell = ss.getActiveCell();
cell.setBackground('#ffff55');// replace by cell.setBackground(null); to reset the color when "leaving" the cell
var activeR = cell.getRow()-1;
var activeC = cell.getColumn()-1;
var value = cell.getValue();
var data = ss.getDataRange().getValues();
var step = 0
for(var r=activeR;r<data.length;++r){
for(var c=activeC;c<data[0].length;++c){
step++
Logger.log(step+' -- '+value+' = '+data[r][c]);
if(data[r][c]==''||step==1){ continue };
if(value.toString().toLowerCase()==data[r][c].toString().toLowerCase()){
ss.getRange(r+1,c+1).activate().setBackground('#ffff55');
return;
}
}
}
}
Caveat
This code only searches 'downwards', i.e. any occurrence in a row that would precede the selected cell is ignored, same for columns...
If that's an issue for you then the code should be modified to start iterating from 0. But in this case if one need to ignore the initial starting cell then you should also memorize its coordinates and skip this value in iteration.

Get and use user data from another sheet in Google Docs

We use google spreadsheets for reporting by quite a big number of users.
I have written a basic script, which opens a specific sheet depending on the current user:
var CurrentUser = Session.getUser().getEmail()
var ss = SpreadsheetApp.getActive();
switch(CurrentUser){
case "usermail1#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet1"));
break;
case "usermail2#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet2"));
break;
case "usermail3#gmail.com":
SpreadsheetApp.setActiveSheet(ss.getSheetByName("sheet3"));
break;
etc...
I would like to put the userdata and sheetnames into an external table and get these data depending on that table, so it is easier to maintain the list of e-mails and users.
How can I get data from a specific google spreadsheet and let the script work according to that?
You can try this. It simulates a VLOOKUP on a different sheet and switches to the 'matched' sheet in your current workbook. This doesn't handle non-matches, but that should be relatively straightforward to add to suit your case.
function switchSheets() {
// Current sheet
var ss = SpreadsheetApp.getActive();
// Target sheet (using the key found in the URL)
var target = SpreadsheetApp.openById("my_other_sheet_id");
var rows = target.getDataRange();
var values = rows.getValues();
// Get the current user
var CurrentUser = Session.getUser().getEmail();
// Now iterate through all of the rows in your target sheet,
// looking for a row that matches your current user (this assumes that
// it will find a match - you'll want to handle non-matches as well)
for (var i = 1; i <= rows.getNumRows() - 1; i++) {
// Match the leftmost column
var row = values[i][0];
if (row == CurrentUser) {
// If we match, grab the corresponding sheet (one column to the right)
target_sheet = values[i][1];
break;
}
}
// Now switch to the matched sheet (rememeber to handle non-matches)
ss.setActiveSheet(ss.getSheetByName(target_sheet));
};

Insert image into a specified location

I have a Google Apps script which replaces placeholders in a copy of a template document with some text by calling body.replaceText('TextA', 'TextB');.
Now I want to extend it to contain images. Does anybody have idea how to do this?
Thank you,
Andrey
EDIT: Just to make it clear what my script does. I have a Google form created in a spreadsheet. I've created a script which runs upon form submission, traverses a sheet corresponding to the form, find unprocessed rows, takes values from corresponding cells and put them into a copy of a Google document.
Some fields in the Google form are multi-line text fields, that's where '\r\r' comes from.
Here's a workaround I've come up with by now, not elegant, but it works so far:
// replace <IMG src="URL"> with the image fetched from URL
function processIMG_(Doc) {
var totalElements = Doc.getNumChildren();
for( var j = 0; j < totalElements; ++j ) {
var element = Doc.getChild(j);
var type = element.getType();
if (type =='PARAGRAPH'){
var par_text = element.getText();
var start = par_text.search(new RegExp('<IMG'));
var end = par_text.search(new RegExp('>'));
if (start==-1)
continue;
// Retrieve an image from the web.
var url = getURL_(par_text.substring(start,end));
if(url==null)
continue;
// Before image
var substr = par_text.substring(0,start);
var new_par = Doc.insertParagraph(++j, substr);
// Insert image
var resp = UrlFetchApp.fetch(url);
new_par.appendInlineImage(resp.getBlob());
// After image
var substr = par_text.substring(end+1);
Doc.insertParagraph(++j, substr);
element.removeFromParent();
j -= 2; // one - for latter increment; another one - for increment in for-loop
totalElements = Doc.getNumChildren();
}
}
}
Here is a piece of code that does (roughly) what you want.
(there are probably other ways to do that and it surely needs some enhancements but the general idea is there)
I have chosen to use '###" in the doc to mark the place where the image will be inserted, the image must be in your google drive (or more accurately in 'some' google drive ).
The code below uses a document I shared and an image I shared too so you can try it.
here is the link to the doc, don't forget to remove the image and to put a ### somewhere before testing (if ever someone has run the code before you ;-)
function analyze() { // just a name, I used it to analyse docs
var Doc = DocumentApp.openById('1INkRIviwdjMC-PVT9io5LpiiLW8VwwIfgbq2E4xvKEo');
var image = DocsList.getFileById('0B3qSFd3iikE3cF8tSTI4bWxFMGM')
var totalElements = Doc.getNumChildren();
var el=[]
for( var j = 0; j < totalElements; ++j ) {
var element = Doc.getChild(j);
var type = element.getType();
Logger.log(j+" : "+type);// to see doc's content
if (type =='PARAGRAPH'){
el[j]=element.getText()
if(el[j]=='###'){element.removeFromParent();// remove the ###
Doc.insertImage(j, image);// 'image' is the image file as blob
}
}
}
}
EDIT : for this script to work the ### string MUST be alone in its paragraph, no other character before nor after... remember that each time one forces a new line with ENTER the Document creates a new paragraph.

Resources