Trouble getting App Script to read data in multiple ranges - javascript

I am currently trying to get App Script to read a ranges of cells from different sheets in a workbook, but I keep getting "Range not found (line 30). I have a bit of experience with Javascript, but App Script is fairly new to me, so some tips and where to find good documentation or tutorials would really help as well.
function getvalues(values2) {
var sheet = SpreadsheetApp.getActiveSheet().getRange(values2)
var raw_values = []
for (var i = 0; i < sheet.length; i++) {
raw_values.push(sheet[i])
}
return raw_values
}
Edit
Here is what the value2 is. They're ranges separated by commas, and each range is from a different sheet in the workbook.
January!W22:W35,February!W22:W35,March!W22:W35,April!W22:W35,May!W22:W35,June!W22:W35,July!W22:W35,August!W22:W35,September!W22:W35,October!W22:W35,November!W22:W35,December!W22:W35

According to the docs, getRange() doesn't seem to specify a comma separated list of ranges as a valid argument. The error is most likely because the function is trying to interpret the entire string as a single range.
You may need to retrieve each range separately and then aggregate them in some way.
I've included an example of how you might do this.
let myRanges = "January!W22:W35,February!W22:W35,March!W22:W35"
function getvalues(values2) {
let raw_values = []
values2.split(",").forEach(a1range => {
let range = SpreadsheetApp.getActiveSheet().getRange(a1range)
console.log(range.getValues())
// get the values from the range and push them to raw_values
})
return raw_values
}
getvalues(myRanges)
Additionally, it looks like getRange() will return a Range object, which doesn't directly contain the values from the range, but has the methods getValue() and getValues() to return the values from the selected cells. getValues appears to return a multidimensional array representing the rows/columns from your range, so you'll need to do some extra processing depending on how you need your data formatted.
https://developers.google.com/apps-script/reference/spreadsheet/range#getValue()
Considering that, it would probably also be a good idea to change the function name to something other than getvalues to avoid confusion.

Related

How Do I Copy Non-Adjacent Cell Values to a Row for a Log Sheet?

I want to copy three cells, which are non-adjacent (AA9, AA12, and AA15), and paste them on another sheet in a single row, like a log sheet. (A2,B2,C2) I'm using "getLastRow+1" to make this happen. Currently, I'm only able to use getValue and setValue for a single cell (AA9) and after searching for similar examples online, I'm still struggling to make this work.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Main")
var destSheet = ss.getSheetByName('Log');
// Get the Cell Values from Main Sheet
var streak = sheet.getRange('AA9').getValue();
// Set the Values on Log Sheet
destSheet.getRange(destSheet.getLastRow()+1,1).setValue(streak);
I'm struggling to understand how to correctly implement what I want. I essentially want to copy the values in AA9, AA12, and AA15, and paste them (or use setValue) in A2,B2, and C2 on the other sheet.
I'm slowly learning and getting a better understanding of everything, and examples from google searches have gotten me far, but this is a headscratcher for me.
I've tried using Named Ranges, but I didn't understand how to find the last EMPTY row of that named range.
I've tried creating an Array for those cells, but didn't understand how to use SetValues correctly (got an error about the number of rows not being equal to the data rows, something like that)
// THIS DIDN"T WORK
var results = ['AA9','AA12','AA15'];
var streak = sheet.getRangeList(results).getRanges().map(range => range.getValues());
destSheet.getRange(destSheet.getLastRow()+1,1).setValues(streak);
I've also searched google quite a bit, and while the results have helped me script a random number generator (not shown here), I still struggle with more basic tasks.
You can make your latter code example work by using Range.getValue() instead of Range.getValues().
To easily append the values to the destination sheet, use Sheet.appendRow(), like this:
const sheet = ss.getSheetByName('Main');
const destSheet = ss.getSheetByName('Log');
const data = sheet.getRangeList(['AA9', 'AA12', 'AA15'])
.getRanges()
.map(range => range.getValue());
destSheet.appendRow(data);
Try it this way:
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet0');
const dsh = ss.getSheetByName('Sheet1');
const [a,,,b,,,c] = sh.getRange("AA9:AA15").getValues().flat();
dsh.appendRow([a,b,c]);
}
When I use getValues, it works perfectly! But it does throw an error, saying "Exception: The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues.". But again, it does actually work on my spreadsheet.
When I use getValue, it strangely works, and doesn't throw an error, it sets the values appropriately in an appended row, but then loops back around again and repeatedly sets the value in the first cell again, albeit one row down (because it just filled the previous row so it moves down). It's so strange. I can see it do it slowly, it sets the values in the row, a split second later it loops back to the first value, but one row down, and sets that value again. But none of the other ones.
If you can visualize this, it'll post 3 numbers, correctly, appended in the row, then one row down it'll post only 1 number, but leave the other two blank.
Screenshot Example
function log_data() {
const data = sheet.getRangeList(['AA9','AA12','AA15'])
.getRanges()
.map(range => range.getValue());
destSheet.appendRow(data);
destSheet.getRange(destSheet.getLastRow()+1,1).setValue(data);
}

Google Sheet Script Editor - setValues for array

I`m trying to replace old values with new values using setValues in Google sheet script.
The data is in the below link...
https://docs.google.com/spreadsheets/d/1pSUVkxM9FhSNgizedHbY2MnYGTnC2iiYLfrWsoPmDks/edit?usp=sharing
I`m basically trying to remove first 14 characters and the last 12 characters under "Tracker" column
Below is the code I tried..
function URLReplacement() {
var ss = SpreadsheetApp.getActive().getSheetByName("transformer");
var rng = ss.getRange("G:G");
var data = rng.getValues();
for (var items in data)
{
var newer = data[items][0].substring(14)
// Turn these strings into an array
var newerr = newer.split(" ")
// Turn this into 2 dimensional array to use setValues
ss.getRange("G:G").setValues([newerr])
Logger.log([newer]);
}
}
But now, I get errors with the setValues statement
Saying the range I set there do not match the data
What am I doing wrong here..?
Can anyone please provide me with suggestions / advice?
You want to convert from IMAGE_SUFFIX_"http://google.com"<xxxnouse>" to http://google.com at the column "G".
The format of IMAGE_SUFFIX_"http://google.com"<xxxnouse>" is constant.
If my understanding is correct, how about this modification? The reason of your error is that [newer] is not 2 dimensional array for using setValues(). If this error was removed, the header is removed by overwriting the empty value. So I would like to modify as follows.
Modification points:
When getLastRow() is used, the data size retrieved by it can be reduced from that retrieved by "G:G". By this, the process cost can be reduced.
Header is not retrieved by getRange(2, 7, ss.getLastRow(), 1).
From the format of IMAGE_SUFFIX_"http://google.com"<xxxnouse>", split() was used for parsing this value.
The converted data was put by setValues(). By this, the process cost can be also reduced.
Modified script:
function URLReplacement() {
var ss = SpreadsheetApp.getActive().getSheetByName("transformer");
var rng = ss.getRange(2, 7, ss.getLastRow(), 1); // Modified
var data = rng.getValues();
var convertedData = data.map(function(e) {return e[0] ? [e[0].split('"')[1]] : e}); // Added
rng.setValues(convertedData); // Added
}
Note:
In your shared sample Spreadsheet, the sheet name is "Sheet1". But your script uses "transformer" as the sheet name. Please be careful this.
If the format of actual values in your Spreadsheet is different from your shared Spreadsheet, this might not be able to be used.
References:
split()
setValues()
If this was not the result you want, I apologize.

How do I pass multiple columns into a formula in Google Spreadsheets?

I'm creating a new spreadsheet for daytrading, where I want to automate the calculations for every row. I have already looked at Google's custom function guide which deals with only one column. I have multiple columns whose values need to be taken into account. For example, I want:
Column D1:D = (Column A1:A x Column B:B) - (Column C1:C)
How do I do this using custom functions only since they are cleaner and more easier to read (I have gotten this to work without using a custom formula and the solution is not readable, maintainable, or elegant)?
Edit: I'm familiar with this recursive formula from Google's guide on custom formulas but I don't know how to modify it for multiple columns:
function DOUBLE(input) {
if(input.map) {
return input.map(DOUBLE);
} else {
return input * 2;
}
}
You can write custom functions that take arrays as parameters. Here is an example of a function that should multiply 2 columns and then subtract a third, as in your example (I haven't tested it, and it assumes that all arrays are the same size without error checking but this is just for you to get the idea):
function multAndSub(ary1, ary2, ary3) {
var resultAry = [];
for (var row=0; row<ary1.length; row++)
resultAry.push(ary1[row]*ary2[row]-ary3[row]);
return resultAry;
}
After you write the function, you can call it as any other formula from the spreadsheet:
=multAndSub(A:A, B:B, C:C)

indexOf returning -1 despite object being in the array - Javascript in Google Spreadsheet Scripts

I am writing a script for a Google Docs Spreadsheet to read a list of directors and add them to an array if they do not already appear within it.
However, I cannot seem to get indexOf to return anything other than -1 for elements that are contained within the array.
Can anyone tell me what I am doing wrong? Or point me to an easier way of doing this?
This is my script:
function readRows() {
var column = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("Director");
var values = column.getValues();
var numRows = column.getNumRows();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var directors = new Array();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (directors.indexOf(row) == -1) {
directors.push(row);
} else {
directors.splice(directors.indexOf(row), 1, row);
}
}
for (var i = 2; i < directors.length; i++) {
var cell = sheet.getRange("F" + [i]);
cell.setValue(directors[i]);
}
};
When you retrieve values in Google Apps Script with getValues(), you will always be dealing with a 2D Javascript array (indexed by row then column), even if the range in question is one column wide. So in your particular case, and extending +RobG's example, your values array will actually look something like this:
[['fred'], ['sam'], ['sam'], ['fred']]
So you would need to change
var row = values[i];
to
var row = values[i][0];
As an aside, it might be worth noting that you can use a spreadsheet function native to Sheets to achieve this (typed directly into a spreadsheet cell):
=UNIQUE(Director)
This will update dynamically as the contents of the range named Director changes. That being said, there may well be a good reason that you wanted to use Google Apps Script for this.
It sounds like an issue with GAS and not the JS. I have always had trouble with getValues(). Even though the documentation says that it is a two dimensional array, you can't compare with it like you would expect to. Although if you use an indexing statement like values[0][1] you will get a basic data type. The solution (I hope there is a better way) is to force that object into a String() and then split() it back into an array that you can use.
Here is the code that I would use:
var column = SpreadsheetApp.getActiveSpreadsheet().getRangeByName("Director");
var values = column.getValues();
values = String(values).split(",");
var myIndex = values.indexOf(myDirector);
If myDirector is in values you will get a number != -1. However, commas in your data will cause problems. And this will only work with 1D arrays.
In your case: var row = values[i]; row is an object and not the string that you want to compare. Convert all of your values to an array like I have above and your comparison operators should work. (try printing row to the console to see what it says: Logger.log(row))
I ran into a similar problem with a spreadsheet function that took a range as an object. In my case, I was wanting to do a simple search for a fixed set of values (in another array).
The problem is, your "column" variable doesn't contain a column -- it contains a 2D array. Therefore, each value is it's own row (itself an array).
I know I could accomplish the following example using the existing function in the spreadsheet, but this is a decent demo of dealing with the 2D array to search for a value:
function flatten(range) {
var results = [];
var row, column;
for(row = 0; row < range.length; row++) {
for(column = 0; column < range[row].length; column++) {
results.push(range[row][column]);
}
}
return results;
}
function getIndex(range, value) {
return flatten(range).indexOf(value);
}
So, since I wanted to simply search the entire range for the existance of a value, I just flattened it into a single array. If you really are dealing with 2D ranges, then this type of flattening and grabbing the index may not be very useful. In my case, I was looking through a column to find the intersection of two sets.
Because we are working with a 2D array, 2dArray.indexOf("Search Term") must have a whole 1D array as the search term. If we want to search for a single cell value within that array, we must specify which row we want to look in.
This means we use 2dArray[0].indexOf("Search Term") if our search term is not an array. Doing this specifies that we want to look in the first "row" in the array.
If we were looking at a 3x3 cell range and we wanted to search the third row we would use 2dArray[2].indexOf("Search Term")
The script below gets the current row in the spreadsheet and turns it into an array. It then uses the indexOf() method to search that row for "Search Term"
//This function puts the specified row into an array.
//var getRowAsArray = function(theRow)
function getRowAsArray()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); // Get the current spreadsheet
var theSheet = ss.getActiveSheet(); // Get the current working sheet
var theRow = getCurrentRow(); // Get the row to be exported
var theLastColumn = theSheet.getLastColumn(); //Find the last column in the sheet.
var dataRange = theSheet.getRange(theRow, 1, 1, theLastColumn); //Select the range
var data = dataRange.getValues(); //Put the whole range into an array
Logger.log(data); //Put the data into the log for checking
Logger.log(data[0].indexOf("Search Term")); //2D array so it's necessary to specify which 1D array you want to search in.
//We are only working with one row so we specify the first array value,
//which contains all the data from our row
}
If someone comes across this post you may want to consider using the library below. It looks like it will work for me. I was getting '-1' return even when trying the examples provide (thanks for the suggestions!).
After adding the Array Lib (version 13), and using the find() function, I got the correct row!
This is the project key I used: MOHgh9lncF2UxY-NXF58v3eVJ5jnXUK_T
And the references:
https://sites.google.com/site/scriptsexamples/custom-methods/2d-arrays-library#TOC-Using
https://script.google.com/macros/library/d/MOHgh9lncF2UxY-NXF58v3eVJ5jnXUK_T/13
Hopefully this will help someone else also.
I had a similar issue. getValues() seems to be the issue. All other methods were giving me an indexOf = -1
I used the split method, and performed the indexOf on the new array created. It works!
var col_index = 1;
var indents_column = main_db.getRange(1,col_index,main_db.getLastRow(),1).getValues();
var values = String(indents_column).split(","); // flattening the getValues() result
var indent_row_in_main_db = values.indexOf(indent_to_edit) + 1; // this worked
I ran into the same thing when I was using
let foo = Sheet.getRange(firstRow, dataCol, maxRow).getValues();
as I was expecting foo to be a one dimensional array. On research for the cause of the apparently weird behavior of GAS I found this question and the explanation for the always two dimensional result. But I came up with a more simple solution to that, which works fine for me:
let foo = Sheet.getRange(firstRow, dataCol, maxRow).getValues().flat();

Google Apps Scripting - Generic Headers in Spreadsheet

Quick look where I normally bother people tells me here is the new place to ask questions!
I have been making a script to create documentation generated from spreadsheet data which was in turn generated from a Google form. (Hope that makes sense...)
Anyway, I have been very successful with a lot of searching and bit of help but now I want to make my script homogeneous so I don't need to tinker with it when I want to setup new forms etc.
I have the getRowData function going from Googles script tutorials but instead of calling the row data from the normalised Headers i would like these to be generic i.e. Column1, Column2 etc.
I've pasted the tutorial function below, it passes the data to another function which normalizes the headers to use as objects, I was thinking thats where I could make them generic but I'm not sure how to get started on it...
Any help would be greatly appreciated!!
Thanks,
Alex
// getRowsData iterates row by row in the input range and returns an array of objects.
// Each object contains all the data for a given row, indexed by its normalized column name.
// Arguments:
// - sheet: the sheet object that contains the data to be processed
// - range: the exact range of cells where the data is stored
// - columnHeadersRowIndex: specifies the row number where the column names are stored.
// This argument is optional and it defaults to the row immediately above range;
// Returns an Array of objects.
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
// Browser.msgBox(headers.toSource());
return getObjects(range.getValues(), normalizeHeaders(headers));
// return getObjects(range.getRowIndex);
}
If you want to get the columns using their index, why parse them to object at all? Just use the plain getValues!
var values = sheet.getDataRange().getValues();
var row2 = values[1];
var cell_in_col4 = row2[3];
It looks like you are missing "var" when declaring your columnHeadersRowIndex variable.

Categories

Resources