Arduino google sheet integration - google-sheets

I'm doing a project with Arduino, I'm trying to post variable's data into google sheet integration but the code doesn't work.
I tried to correct it, but it doesn't post anyway....this is the code.
The error was
ss.sheet.getSheetByName it's not a function
I took the code from Arduino IoT Cloud Google sheet Integration
function myFunction() {
// get sheet named RawData
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Dati");
var MAX_ROWS = 1440; // max number of data rows to display
// 3600s / cloud_int(30s) * num_ore(12h)
var HEADER_ROW = 1; // row index of header
var TIMESTAMP_COL = 1; // column index of the timestamp column
function doPost(e) {
var cloudData = JSON.parse(e.postData.contents); // this is a json object containing all info coming from IoT Cloud
//var webhook_id = cloudData.webhook_id; // really not using these three
//var device_id = cloudData.device_id;
//var thing_id = cloudData.thing_id;
var values = cloudData.values; // this is an array of json objects
// store names and values from the values array
// just for simplicity
var incLength = values.length;
var incNames = [];
var incValues = [];
for (var i = 0; i < incLength; i++) {
incNames[i] = values[i].name;
incValues[i] = values[i].value;
}
// read timestamp of incoming message
var timestamp = values[0].updated_at; // format: yyyy-MM-ddTHH:mm:ss.mmmZ
var date = new Date(Date.parse(timestamp));
/*
This if statement is due to the fact that duplicate messages arrive from the cloud!
If that occurs, the timestamp is not read correctly and date variable gets compromised.
Hence, execute the rest of the script if the year of the date is well defined and it is greater
then 2018 (or any other year before)
*/
if (date.getYear() > 2018) {
// discard all messages that arrive 'late'
if (sheet.getRange(HEADER_ROW+1, 1).getValue() != '') { // for the first time app is run
var now = new Date(); // now
var COMM_TIME = 5; // rough overestimate of communication time between cloud and app
if (now.getTime() - date.getTime() > COMM_TIME * 1000) {
return;
}
}
// this section write property names
sheet.getRange(HEADER_ROW, 1).setValue('timestamp');
for (var i = 0; i < incLength; i++) {
var lastCol = sheet.getLastColumn(); // at the very beginning this should return 1 // second cycle -> it is 2
if (lastCol == 1) {
sheet.getRange(HEADER_ROW, lastCol + 1).setValue(incNames[i]);
} else {
// check if the name is already in header
var found = 0;
for (var col = 2; col <= lastCol; col++) {
if (sheet.getRange(HEADER_ROW, col).getValue() == incNames[i]) {
found = 1;
break;
}
}
if (found == 0) {
sheet.getRange(HEADER_ROW, lastCol+1).setValue(incNames[i]);
}
}
}
// redefine last column and last row since new names could have been added
var lastCol = sheet.getLastColumn();
var lastRow = sheet.getLastRow();
// delete last row to maintain constant the total number of rows
if (lastRow > MAX_ROWS + HEADER_ROW - 1) {
sheet.deleteRow(lastRow);
}
// insert new row after deleting the last one
sheet.insertRowAfter(HEADER_ROW);
// reset style of the new row, otherwise it will inherit the style of the header row
var range = sheet.getRange('A2:Z2');
//range.setBackground('#ffffff');
range.setFontColor('#000000');
range.setFontSize(10);
range.setFontWeight('normal');
// write the timestamp
sheet.getRange(HEADER_ROW+1, TIMESTAMP_COL).setValue(date).setNumberFormat("yyyy-MM-dd HH:mm:ss");
// write values in the respective columns
for (var col = 1+TIMESTAMP_COL; col <= lastCol; col++) {
// first copy previous values
// this is to avoid empty cells if not all properties are updated at the same time
sheet.getRange(HEADER_ROW+1, col).setValue(sheet.getRange(HEADER_ROW+2, col).getValue());
for (var i = 0; i < incLength; i++) {
var currentName = sheet.getRange(HEADER_ROW, col).getValue();
if (currentName == incNames[i]) {
// turn boolean values into 0/1, otherwise google sheets interprets them as labels in the graph
if (incValues[i] == true) {
incValues[i] = 1;
} else if (incValues[i] == false) {
incValues[i] = 0;
}
sheet.getRange(HEADER_ROW+1, col).setValue(incValues[i]);
}
}
}
} // end if (date.getYear() > 2018)
}
}

You are trying to push data directly to the Google sheet on the cloud but it's URL uses https for security reasons. The encryption for https is rather complex and SSL crypto is required. Arduino hardware is normally not fast enough to do SSL. You need more powerful hardware like Raspberry Pi to write directly to the Google Cloud.
However, you can use PushingBox, a cloud that can send notifications based on API calls, to do the hard work for you if you insist on using Arduino ( any variant) in your IOT project. Here you send your data to Pushingbox, it uses http URL, and it will in turn the data to Google sheet.

Related

Google Sheets - Unmerge cells and fill down

I need to take a sheet maintained by someone else and do the following (so that I can export to a csv):
unmerge all cells
fill values down
merged cells are in multiple columns, so I need to iterate over a range
It's too much to do it by hand, and it will need done periodically. My javascript and google sheets object model knowledge approximate zero, but I know it's possible because I could do it in VBA. I searched but can only find programmatic answers for VBA/Excel.
How can I do this efficiently in Google Sheets?
You can use the breakapart() class to do this. I am assuming that the merged range is not static and has multiple occurrences. This script will unmerge all merged ranges in the active sheet.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(1, 1, sheet.getMaxRows(), sheet.getMaxColumns()).breakApart();
}
Adapted from #lreeder's answer
The following breaks and fill blank with above on the selected range:
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('BreakAndFill')
.addItem('Break Fill Blank Cells', 'menuItem1')
.addToUi();
}
function menuItem1() {
BreakandfillBlankWithAbove()
}
//Breaks range
//Iterates over the range from top to bottom
//and left to right, and fills blank cells
//with the value right above them.
function BreakandfillBlankWithAbove() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveRange();
Logger.log('Range height is: ' + range.getHeight());
var values = range.getValues();
range.breakApart();
var width = values[0].length;
var height = values.length;
Logger.log('Width is ' + width);
Logger.log('Height is ' + height);
for (var i = 0; i < width; i++) {
var lastVal = '';
for(var j = 0; j < height; j++) {
var currValue = values[j][i];
var dataType = typeof(currValue);
//Empty string check returns true if dataType
//is a number, and value is zero. In that case
//we don't want to overwrite the zero, so only
//check emptyString for string types.
if(currValue === undefined ||
(dataType === 'string' && currValue == '')
) {
//getCell parameters are relative to the current range
//and ones based
var cell = range.getCell(j+1, i+1);
cell.setValue(lastVal);
}
else {
lastVal = currValue;
}
}
}
SpreadsheetApp.flush();
}

Sending an email automatically when the edited cell value says "Done" and the email subject is the first cell in the edited row

I'm trying to write a script where an automatic email is sent to a specific person upon edit of a cell and the cell equals "Done". However, I want the subject to be the first cell of the edited row. Cell that will contain "Done" is always going to be in the AA Column, and I want the subject to be the A column of the same row. Ex: AA3 was edited so subject is A3. I have spent hours sifting through tutorials and came up with this:
function checkValue() {
var sp = PropertiesService.getScriptProperties();
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName("Accts");
var valueToCheck = sheet.getRange("AA2:AA1000").getValue();
if (valueToCheck = 'Done') {
MailApp.sendEmail("a***a#gmail.com", activeCell.offset(-26,0).getValue(), Email.html);
}
}
Am I doing this entirely wrong or is there hope?
EDIT:
Now that it's resolved. I thought I'd share what my script ended up looking like. I added a UI and an option to execute using a menu option. Hope this helps someone else.
function onEdit(e)
{
var editRange = { // AA2:AA1000
top : 2,
bottom : 1000,
left : 27,
right : 27
};
// Exit if we're out of range
var thisRow = e.range.getRow();
if (thisRow < editRange.top || thisRow > editRange.bottom) return;
var thisCol = e.range.getColumn();
if (thisCol < editRange.left || thisCol > editRange.right) return;
var thisthang = e.value;
var doit = 'TRUE'
// We're in range; timestamp the edit
if(thisthang == doit)
{
doFinish();
}
else{return};
}
function onOpen()
{
var ui = SpreadsheetApp.getUi();
ui.createMenu('Finished')
.addItem('Finish', 'doFinish')
.addToUi();
}
function doFinish()
{
var cell = SpreadsheetApp.getActiveSheet().getActiveCell();
var row = cell.getRow();
var Campaign = getCampaignFromRow(row, 1);
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Finish '+Campaign.name+'?', ui.ButtonSet.YES_NO);
if(response == ui.Button.YES)
{
handleFinish(row, Campaign);
}
if(response == ui.Button.NO)
{
SpreadsheetApp.getActiveSheet().getRange(row, 27).setValue('FALSE');
}
}
function getCampaignFromRow(row)
{
var values = SpreadsheetApp.getActiveSheet().getRange(row, 1).getValues();
var rec = values[0];
var Campaign =
{
Campaign_Name: rec[0]
};
Campaign.name = Campaign.Campaign_Name;
return Campaign;
}
function handleFinish(row, Campaign)
{
var templ = HtmlService
.createTemplateFromFile('Campaign-email');
templ.Campaign = Campaign;
var message = templ.evaluate().getContent();
MailApp.sendEmail({
to: "a***a#gmail.com",
subject: "A Campaign has been finished!",
htmlBody: message
});
SpreadsheetApp.getActiveSheet().getRange(row, 27).setValue('TRUE');
}
You are trying to trigger an email when the spreadsheet is edited on the "Accts" sheet, in Column "AA" and the value = "Done".
The best solution is to use an onEdit trigger and also make use of Event Objects. In the case, "a simple trigger cannot send an email" ref, so you will need to create an Installable Trigger.
The main differences to your script are:
- var valueToCheck = sheet.getRange("AA2:AA1000").getValue();
- a couple of things here.
- 1) you are trying to get all the values in the column, but you use the getValue (the method for a single cell) instead of getValues.
- 2) you could have defined the ActiveCell and just returned that value
- 3) though you tried to get the values for the entire column, your if statement is designed as though there is a single value rather than an array of values.
- 4) This demonstrates the benefit of the using the Event Objects. You can succinctly get the values of the edited cell and sheet.
- in your if comparison, you use "="; this is only used to assign a value. When comparing values you must use "==" or "===".
- To get the value of the "subject", the script uses the row number derived from the Event Objects; compared to the offset in your script - they are both acceptable. I used the getRange to demonstrate the alternative.
- your email body was defined as "Email.html", but this isn't declared. The answer uses a very simple body but could just as easily use another solution.
function so5967209001(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
// establish the values to be checked
var checkSheetname = "Accts"
var checkValue = "Done"
var checkColumn = 27; // column AA
// this will return the event objects
//Logger.log(JSON.stringify(e)); // DEBUG
// variables to use for checking
var editedrange = e.range;
var editedrow = editedrange.getRow();
var editedcolumn = editedrange.getColumn();
var editedsheet = editedrange.getSheet().getSheetName();
var editedvalue = e.value;
//Logger.log("DEBUG: row = "+editedrow+", column = "+editedcolumn+", sheet = "+editedsheet+", value"+editedvalue)
// test the sheet, the column and the value
if (editedsheet ==checkSheetname && editedcolumn==checkColumn && editedvalue == checkValue){
//Logger.log("DEBUG: this is a match");
var subject = sheet.getRange(editedrow,1).getValue(); // Column A of the edited row
//Logger.log("DEBUG: email subject = "+subject);
// build your own body
var body = "this is the body of the email"
// send the email
MailApp.sendEmail("ejb#tedbell.com.au", subject, body);
//Logger.log("DEBUG: mail sent")
}else{
//Logger.log("DEBUG: this isn't a match");
}
}

For all e-mail entries that do not have a firstname, replace with 'Gamer'

I manage a large email list for my gaming society. In column A, I have the e-mails, and in column B, I have the usernames. I populate column B with a formula that extracts the name of the user from their e-mail address, which is often in the firstname.lastname#email.com form. Column B therefore returns "Firstname" after I run the formula if the user's email is in the firstname.lastname#email.com format.
Sometimes, however, the emails have just the initial of the first name (f.lastname#email.com) and, in these case, I want to have Column B return the word 'Gamer' rather than, for example, the first letter of the user's email.
Here is the script I use at the moment, which current deletes all rows with four or more numbers:
function removeNumbers() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[0].toLowerCase().indexOf("robot") > -1) {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
On your shared spreadsheet, use this
function firstName() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var lr=sheet.getLastRow()
var val = sheet.getRange(2,1,lr-1,2).getValues();//get the col A email addrs
var newVal=[]//new array for before fitst period
for (var i = 0; i <= val.length-1; i++) {
var str=val[i][0].split(".")//split email at period
var len=str[0].length // determine lenght of number string of first split
if(val[i][1]=="inactive"){
newVal.push(["inactive"])
continue
}
if(len<=1){//if 1 or less
def="Gamer"
newVal.push([def]) //put Gamer in new array
}
else{
newVal.push([toTitleCase(str[0])]) //keep first name
}}
sheet.getRange(2, 2, newVal.length, 1).setValues(newVal)//set new values in col B
}
function toTitleCase(str) {
return str.replace(/\w\S*/g, function (txt) {
return txt.charAt(0)
.toUpperCase() + txt.substr(1)
.toLowerCase();
});
}
This well also run on the active sheet.

Google docs to google sheets script

I’m using Google Sheets to conduct some text analysis. I would like to automate the process by linking a spreadsheet to a Google doc file on my GDrive to extract text directly. It doesn’t have to structured/formatted. It just has to be a plain text. Does it take a comprehensive scripting or the task of copying text is simple?
I searched the web but couldn’t find one.
To help you start, here is an SO thread.
Same scenario: Get data from the Google Sheets and copy it to the Google Docs.
A reference from Open Source Hacker: Script for generating Google documents from Google spreadsheet data source.
Here is the code provided in the article. You can modify it by yourself depending on your use.
/**
* Generate Google Docs based on a template document and data incoming from a Google Spreadsheet
*
* License: MIT
*
* Copyright 2013 Mikko Ohtamaa, http://opensourcehacker.com
*/
// Row number from where to fill in the data (starts as 1 = first row)
var CUSTOMER_ID = 1;
// Google Doc id from the document template
// (Get ids from the URL)
var SOURCE_TEMPLATE = "xxx";
// In which spreadsheet we have all the customer data
var CUSTOMER_SPREADSHEET = "yyy";
// In which Google Drive we toss the target documents
var TARGET_FOLDER = "zzz";
/**
* Return spreadsheet row content as JS array.
*
* Note: We assume the row ends when we encounter
* the first empty cell. This might not be
* sometimes the desired behavior.
*
* Rows start at 1, not zero based!!! 🙁
*
*/
function getRowAsArray(sheet, row) {
var dataRange = sheet.getRange(row, 1, 1, 99);
var data = dataRange.getValues();
var columns = [];
for (i in data) {
var row = data[i];
Logger.log("Got row", row);
for(var l=0; l<99; l++) {
var col = row[l];
// First empty column interrupts
if(!col) {
break;
}
columns.push(col);
}
}
return columns;
}
/**
* Duplicates a Google Apps doc
*
* #return a new document with a given name from the orignal
*/
function createDuplicateDocument(sourceId, name) {
var source = DocsList.getFileById(sourceId);
var newFile = source.makeCopy(name);
var targetFolder = DocsList.getFolderById(TARGET_FOLDER);
newFile.addToFolder(targetFolder);
return DocumentApp.openById(newFile.getId());
}
/**
* Search a paragraph in the document and replaces it with the generated text
*/
function replaceParagraph(doc, keyword, newText) {
var ps = doc.getParagraphs();
for(var i=0; i<ps.length; i++) {
var p = ps[i];
var text = p.getText();
if(text.indexOf(keyword) >= 0) {
p.setText(newText);
p.setBold(false);
}
}
}
/**
* Script entry point
*/
function generateCustomerContract() {
var data = SpreadsheetApp.openById(CUSTOMER_SPREADSHEET);
// XXX: Cannot be accessed when run in the script editor?
// WHYYYYYYYYY? Asking one number, too complex?
//var CUSTOMER_ID = Browser.inputBox("Enter customer number in the spreadsheet", Browser.Buttons.OK_CANCEL);
if(!CUSTOMER_ID) {
return;
}
// Fetch variable names
// they are column names in the spreadsheet
var sheet = data.getSheets()[0];
var columns = getRowAsArray(sheet, 1);
Logger.log("Processing columns:" + columns);
var customerData = getRowAsArray(sheet, CUSTOMER_ID);
Logger.log("Processing data:" + customerData);
// Assume first column holds the name of the customer
var customerName = customerData[0];
var target = createDuplicateDocument(SOURCE_TEMPLATE, customerName + " agreement");
Logger.log("Created new document:" + target.getId());
for(var i=0; i<columns.length; i++) {
var key = columns[i] + ":";
// We don't replace the whole text, but leave the template text as a label
var text = customerData[i] || ""; // No Javascript undefined
var value = key + " " + text;
replaceParagraph(target, key, value);
}
}

Google Sheets Lookup Tool Assistance

I've taken it upon myself the learn how to "excel". That made a little more sense in my head, but hey.
I have built a "lookup tool" which can be viewed here.
The "Dishwasher search" is working as intended. You can search by noise level or decor panel height to create a list of suitable models then pull the data into an individual search by product code/model no.
I decided to make it a little more difficult and created one for Ovens.
The Layout is like this; Main search>Single Oven Database>Double Oven Database>Built-under oven database.
My goal is to achieve the same search facilities as the "Dishwasher tool", however I have been unsure how to search (Vlookup) from different sources.
I have tried creating a "Master DB" using the formula below;
={Importrange("1mY13e-75dBYfKgkjV8dFFFEvxC838nGNxPrUdusc0PA", "'Single Ovens'!$A:$F");Importrange("1mY13e-75dBYfKgkjV8dFFFEvxC838nGNxPrUdusc0PA", "'Double Ovens'!$A:$F");Importrange("1mY13e-75dBYfKgkjV8dFFFEvxC838nGNxPrUdusc0PA", "'Built-Under Ovens'!$A:$F")))}
However, it only seems to pull data from the first range not the others unless I do it horizontaly rather than vertical, but horizontal wont work with my Vlookup formula (To my knowledge).
The other method I have tried is using this code;
=IF(AND($A$19="Single Oven",$A$4>0), Vlookup($A$4,'Single Ovens'!$B:$F,1,False),IF(AND($A$10="Double Oven",$A$4>0), Vlookup($A$4,'Double Ovens'!$B:$F,1,False),If(AND($A$10="Built-Under Oven",$A$4>0), Vlookup($A$4,'Built-Under Ovens'!$B:$F,1,False),IF($A$10="Single Oven",Vlookup($A$7,'Single Ovens'!$A:$F,2,False),IF($A$10="Double Oven", Vlookup($A$7,'Double Oven'!$A:$F,2,False),If($A$10="Built-Under Oven", Vlookup($A$7,'Built-Under Oven'!$A:$F,2,False)))))))
The above code was inserted and was "meant" to Vlookup all 3 sheets and pull "product Code" through to populate Cell D4 on sheet 'Ovens'.
Now, I'm a bit of a novice at this but I'm working to understand it all :)
Any suggestions you guys can make or point me in the right direction?
Edit -
Sorry guys. It was rude not to post my solution. I have changed my Importrange function to =Importrange("XYZ",""'Single Ovens'!$A2:$F200") and repeated this 3 times with a gap of 200 "rows" between each one. Not a solution, but a viable alternative. My friend is currently working on a script for me to achieve this. The Vlookup formula no longer needs to be as complex thanks to the importrange formula simplifying matters.
So after discussing the problem with McSheehy, and the problem is actually this.
How to get data from ONE spreadsheet, multiple sheets of my choice.
and write To MANY spreadsheets, Multiple sheets within those spreadsheets, of my choice.
Once that data is in the right place, the current formulas should work or can be adapted easily.
I came up with a script solution,
The user creates a settings sheet in the source sheet.
In A2 downwards, Target spreadsheet keys, B2 downwards, Source sheet names you wish to include from current sheet. C2 downwards is the target SHEET names, if you wanted to write data to more than one sheet.
Bits of code are annotated to help explain McSheehy's questions on how it works.
If anyone has any improvements to suggest, and I'm sure there are some, particular the headers bit. (its not needed, but my clearContent/clearConents line kept flipping out), I'm all ears.
Thanks
function getOvenDataV5(){
var settingsSheetName = "monkey_settings";
/* DO NOT EDIT BELOW THIS LINE */
var ss = SpreadsheetApp.getActiveSpreadsheet();
var settings = ss.getSheetByName(settingsSheetName);
// this bit has been edited, note the getValues, not getValue, as we want the whole column now not just a single cell.
var targetSheetsValues = settings.getRange("C2:C").getValues(); // this gets the target sheet names from the settings sheet
var targetSheets = []; // And array added to throw target sheet names into, as there is more than one.
// the reason we use arrays and loops (later on), is because the script has no idea how much data to expect.
// so we go through whatever it's grabbed, the stuff it thinks is data, but we check it later on.
// only a simple check. Our check is that it cannot be blank. ""
// then stuff it in an array, a handy thing to store data, for use later on.
var sSheets = settings.getRange("B2:B").getValues();
var sourceSheets = [];
// new loop below to get the target sheets names. We'll use this in the write bit later.
for(var i = 0; i < targetSheetsValues.length;i++){
if(targetSheetsValues[i][0]!=""){
targetSheets.push(targetSheetsValues[i]);
}
}
for(var i = 0; i < sSheets.length;i++){
if(sSheets[i][0]!=""){
sourceSheets.push(sSheets[i]);
}
}
var dKeys = settings.getRange("A2:A").getValues();
var sKeys = [];
for(var i = 0; i < dKeys.length;i++){
if(dKeys[i][0]!=""){
sKeys.push(dKeys[i]);
}
}
var data = [];
for (var i = 0; i < sourceSheets.length;i++){
var values = ss.getSheetByName(sourceSheets[i]).getDataRange().getValues();
for (var x = 1;x < values.length; x++){
if(values[x][0]!= ""){
data.push(values[x]);
}
}
}
// Below is an array of your column headers, the script was being annoying when clearing sheet data, so decided to clear the whole damn sheet
// then write the headers via here instead
var headers = [["Model No", "Product Code", "Brand", "Model No", "kW", "Amp"]];
for (var i = 0; i< sKeys.length;i++){
var tss = SpreadsheetApp.openById(sKeys[i]);
for(var x = 0; x < targetSheets.length;x++){ // this loop, within the keys loop, goes through the target sheets array
var target = tss.getSheetByName(targetSheets[x]); // this loads the target sheet, one by one
var range = target.getRange(2,1, data.length, data[0].length); // this gets the cells to write to
target.clearContents(); // clear the sheet before writing data
target.getRange("A1:F1").setValues(headers); // write the headers to a1:F1 in target sheet
range.setValues(data); //write the data
}
}
}
I dont know if they will be of any value but here are two things that i wrote in script.
The follow is a vlookup script to set or return to memory with hints of query ish abilites.
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//--//Dependent on isEmpty_()
// Script Look-up
/*
Benefit of this script is:
-That google sheets will not continually do lookups on data that is not changing with using this function as it is set with hard values until script is kicked off again.
-Unlike Vlookup you can have it look at for reference data at any Column in the row. Does not have to be in the first column for it to work like Vlookup.
-You can return the Lookup to Memory for further processing by other functions
Useage:
Lookup_(Sheetinfo,"Sheet1!A:B",0,[1],"Sheet1!I1","n","y");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",0,[1],"return","n","n");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",0,[0,1],"return","n","n");
//or
Lookup_(Sheetinfo,"Sheet1!A:B",1,[0],"return","y","n");
//or
Lookup_("cat","Sheet1!A:G",4,[0],"Database!A1","y","y");
-Loads all Locations numbers from J2:J into a variable
--looks for Location Numbers in Column 0 of Referance sheet and range eg "Data!A:G"
---Returns results to Column 3 of Target Sheet and range eg "test!A1" or "1,1"
*/
function Lookup_(Search_Key,RefSheetRange,SearchKey_Ref_IndexOffSet,IndexOffSetForReturn,SetSheetRange,ReturnMultiResults,Add_Note)
{
var RefSheetRange = RefSheetRange.split("!");
var Ref_Sheet = RefSheetRange[0];
var Ref_Range = RefSheetRange[1];
if(!/return/i.test(SetSheetRange))
{
var SetSheetRange = SetSheetRange.split("!");
var Set_Sheet = SetSheetRange[0];
var Set_Range = SetSheetRange[1];
var RowVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Range).getRow();
var ColVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Range).getColumn();
}
var twoDimensionalArray = [];
var data = SpreadsheetApp.getActive().getSheetByName(Ref_Sheet).getRange(Ref_Range).getValues(); //Syncs sheet by name and range into var
for (var i = 0, Il=Search_Key.length; i<Il; i++) // i = number of rows to index and search
{
var Sending = []; //Making a Blank Array
var newArray = []; //Making a Blank Array
var Found ="";
for (var nn=0, NNL=data.length; nn<NNL; nn++) //nn = will be the number of row that the data is found at
{
if(Found==1 && ReturnMultiResults.toUpperCase() == 'N') //if statement for found if found = 1 it will to stop all other logic in nn loop from running
{
break; //Breaking nn loop once found
}
if (data[nn][SearchKey_Ref_IndexOffSet]==Search_Key[i]) //if statement is triggered when the search_key is found.
{
var newArray = [];
for (var cc=0, CCL=IndexOffSetForReturn.length; cc<CCL; cc++) //cc = numbers of columns to referance
{
var iosr = IndexOffSetForReturn[cc]; //Loading the value of current cc
var Sending = data[nn][iosr]; //Loading data of Level nn offset by value of cc
if(isEmpty_(Sending)) //if statement for if one of the returned Column level cells are blank
{
var Sending = "#N/A"; //Sets #N/A on all column levels that are blank
}
if (CCL>1) //if statement for multi-Column returns
{
newArray.push(Sending);
if(CCL-1 == cc) //if statement for pulling all columns into larger array
{
twoDimensionalArray.push(newArray);
var Found = 1; //Modifying found to 1 if found to stop all other logic in nn loop
break; //Breaking cc loop once found
}
}
else if (CCL<=1) //if statement for single-Column returns
{
twoDimensionalArray.push(Sending);
var Found = 1; //Modifying found to 1 if found to stop all other logic in nn loop
break; //Breaking cc loop once found
}
}
}
if(NNL-1==nn && isEmpty_(Sending)) //following if statement is for if the current item in lookup array is not found. Nessessary for data structure.
{
for(var na=0,NAL=IndexOffSetForReturn.length;na<NAL;na++) //looping for the number of columns to place "#N/A" in to preserve data structure
{
if (NAL<=1) //checks to see if it's a single column return
{
var Sending = "#N/A";
twoDimensionalArray.push(Sending);
}
else if (NAL>1) //checks to see if it's a Multi column return
{
var Sending = "#N/A";
newArray.push(Sending);
}
}
if (NAL>1) //checks to see if it's a Multi column return
{
twoDimensionalArray.push(newArray);
}
}
}
}
if (CCL<=1) //checks to see if it's a single column return for running setValue
{
var singleArrayForm = [];
for (var l = 0,lL=twoDimensionalArray.length; l<lL; l++) //Builds 2d Looping-Array to allow choosing of columns at a future point
{
singleArrayForm.push([twoDimensionalArray[l]]);
}
if(!/return/i.test(SetSheetRange))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,singleArrayForm.length,singleArrayForm[0].length).setValues(singleArrayForm);
SpreadsheetApp.flush();
if(/y/i.test(Add_Note))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,1,1).setNote("VLookup Script Ran On: " + Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy hh:mm a") + "\nRange: " + Ref_Sheet + "!" + Ref_Range);
}
}
else
{
return singleArrayForm
}
}
if (CCL>1) //checks to see if it's a multi column return for running setValues
{
if(!/return/i.test(SetSheetRange))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,twoDimensionalArray.length,twoDimensionalArray[0].length).setValues(twoDimensionalArray);
SpreadsheetApp.flush();
if(/y/i.test(Add_Note))
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,1,1).setNote("VLookup Script Ran On: " + Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy hh:mm a") + "\nRange: " + Ref_Sheet + "!" + Ref_Range);
}
}
else
{
return twoDimensionalArray
}
}
}
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//--//Dependent on isEmpty_()
//Find Last Row on Database
function getFirstEmptyRowUsingArray_(sheetname)
{
var data = SpreadsheetApp.getActive().getSheetByName(sheetname).getDataRange().getValues();
for(var n = data.length ; n>0 ; n--)
{
if(isEmpty_(data[n])==false)
{
n++;
break;
}
}
n++;
return (n);
}
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
The Following is a ImportRange script because I hate how =importrange() requires you to "request access" on every new formula. If that importrange is embedded you have to take it out and request access and re-embed it. Also a lot of times the formula just breaks. so:
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
//Script Based ImportRange
//Example importRange_('0AodPsggrbsh1dDNH............','Main4NS','A:G','Common','C7','y')
//Explanation importRange_('Importing Spreadsheet Key or URL','Importing Spreadsheet Tab Name','Importing Spreadsheet Tab's Range','Destination Spreadsheet Tab Name','Destination Spreadsheet Tab's placement','Will add note to the first cell of import')
function importRange_(Source_Key,Source_Sheet,Source_Range,Set_Sheet,Set_Pos,Add_Note)
{
var SourceTypeCheck = Source_Key.indexOf("https://");
if(SourceTypeCheck >= 0)
{
var Load = SpreadsheetApp.openByUrl(Source_Key).getSheetByName(Source_Sheet).getRange(Source_Range).getValues();
var Name = SpreadsheetApp.openByUrl(Source_Key).getName();
}
if(SourceTypeCheck == -1)
{
var Load = SpreadsheetApp.openById(Source_Key).getSheetByName(Source_Sheet).getRange(Source_Range).getValues();
var Name = SpreadsheetApp.openById(Source_Key).getName();
}
var RowVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Pos).getRow();
var ColVal = SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(Set_Pos).getColumn();
if(Add_Note.toUpperCase() == 'Y')
{
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,1,1).setNote("Import Script Updated On: " + Utilities.formatDate(new Date(), "PST", "MM-dd-yyyy hh:mm a")+"\nSS Name: "+Name+"\nRange: "+Source_Sheet+"!"+Source_Range+"\nSS Key: "+ Source_Key);
}
SpreadsheetApp.getActive().getSheetByName(Set_Sheet).getRange(RowVal,ColVal,Load.length,Load[0].length).setValues(Load);
SpreadsheetApp.flush();
SpreadsheetApp.getActiveSpreadsheet().toast('At: '+Set_Sheet+'!'+Set_Pos,'Import Completed:');
}
//~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`~,~`
Same thing stands. If you have and ideas for improvement let me know. If you find a way to integrate it, cool beans, Post some code of how you used it.
:)

Resources