Merging Google spreadsheet range values with google apps script - google-apps-script

Yes I know that this is replicating =concat functionality, but I have a need in a Google Spreadsheet App Script project to put a merge of ranges into a button.
[ [ [ 'Joe Smith' ],
[ 'Joe Johnson' ],
[ '' ],
[ 'Joe Jensen' ] ],
[ [ '(479)123-4567' ],
[ '' ],
[ '(479) 321-1234' ],
[ '479-987-0987' ] ] ]
The above is the array representation of two ranges. Imagine I wanted to merge them into a single new column, with the values space separated, like...
[ [ 'Joe Smith (479)123-4567' ],
[ 'Joe Johnson ' ],
[ '(479) 321-1234' ],
[ 'Joe Jensen 479-987-0987' ],
[ ' (479)123-4567' ] ]
(cleaning up errant spaces for nulls in previous or trailing columns is relatively simple)
What's the most efficient way to do this and account for N number of selected column ranges in Apps Script?
It seems like a needless waste of memory to put what might be thousands or tens of thousands of values into an intermediate array and then loop/merge that.
Is there an easier way I'm missing?
Code leading up to this...
let sel = SpreadsheetApp.getActive().getSelection().getActiveRangeList().getRanges();
let spreadsheet = SpreadsheetApp.getActiveSheet();
// ...
lastCol = null;
firstCol = null;
if (sel.length == 1) {
lastCol = spreadsheet.getActiveRange().getLastColumn();
firstCol = (lastCol - spreadsheet.getActiveRange().getNumColumns() + 1);
} else {
lastCol = spreadsheet.getActiveRange().getLastColumn();
firstCol = (lastCol - sel.length + 1);
};
let arr = [];
arr.push(firstCol);
arr.push(lastCol);
let colArray = missingNumbers(arr).concat(arr).sort();
console.log(colArray);
let range = spreadsheet.getRange(colArray[0], 1, 1, colArray.length).getDataRegion(SpreadsheetApp.Dimension.ROWS).activate();
So basically I'm at an array of ranges, or an array of range values in nested lists if that's the best way.
As you can see I am force-selecting the useful data range, so the columns/rows will always be symmetrical. The if/else on the range selector is just to account for whether the user has drag-selected a single multi-column range, or selected multiple columns with ctrl + click.

Use Array.map(), Array.flat() and Array.join(), like this:
const ranges = SpreadsheetApp.getActiveRangeList().getRanges();
const values = ranges.map(range => range.getDisplayValues()).flat();
const result = values.map(row => row.join(' '));
console.log(result);

Related

Copying Google Sheets table to Google Document - Error with Boolean cells

I am very new to programming google app script, just trying to make some custom function. Sorry if this question is too basic...
I was following a tutorial to copy a table from Google Sheets into Google document but the exact same code the instructor was using did not worked for me.
I was getting this error:
Exception: The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable.
The simplified version of the code is:
function fun4(){
var ss = SpreadsheetApp.openById('17-23aFf6mN5oQrKNwNDy3Zh24_foTN5mXzNkjvd3V5w');
var sheet = ss.getSheets()[0];
var doc = DocumentApp.create('Sample Sheet Data');
var body = doc.getBody();
var numLines = sheet.getLastRow();
var numColumns = sheet.getLastColumn();
var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();
console.log(rowData);
var table = body.appendTable(rowData); // ERROR IN THIS LINE
table.getRow(0).editAsText().setBold(true);
}
After some search I found that the problem was caused by the last column containing Boolean values and changed .getValues() to .getDisplayValues().
It is working now but I am very confused...
How it was working in the instructor code but not in mine?
Why did not work if the output looks to be in the same format (double array?)
Code: (Gives an error when I append the table to doc , but worked in the instructor video)
var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();
console.log(rowData);
Output:
11:36:05 AM Info
[ [ 'NAME', 'EMAIL', 'AGE', 'ACTIVE' ],
[ 'Alex', 'alex#gmail.com', 50, true ],
[ 'Brian', 'brian#gmail.com', 34, false ],
[ 'Julian', 'julian#gmail.com', 42, true ],
[ 'John', 'john#gmail.com', 24, false ] ]
Code:
var rowData = sheet.getRange(1, 1, numLines, numColumns).getDisplayValues();
console.log(rowData);
Output:
11:36:05 AM Info
[ [ 'NAME', 'EMAIL', 'AGE', 'ACTIVE' ],
[ 'Alex', 'alex#gmail.com', '50', 'TRUE' ],
[ 'Brian', 'brian#gmail.com', '34', 'FALSE' ],
[ 'Julian', 'julian#gmail.com', '42', 'TRUE' ],
[ 'John', 'john#gmail.com', '24', 'FALSE' ] ]
I believe your goal as follows.
You want to know the reason the following situation.
After some search I found that the problem was caused by the last column containing Boolean values and changed .getValues() to .getDisplayValues().
It is working now but I am very confused... How it was working in the instructor code but not in mine? Why did not work if the output looks to be in the same format (double array?)
Answer:
About the error at the script of var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();, I thought that the reason of the issue might be due to using V8 runtime. Ref
When V8 runtime is enabled at the script editor, I confirmed that the error of The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable. occurred.
When V8 runtime is disabled at the script editor, I confirmed that no error occurred.
The table can be created using the value of var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues(); and your sample values.
From your question, unfortunately, I cannot understand about the instructor video of Code: (Gives an error when I append the table to doc , but worked in the instructor video). But, above situation, I guess that the instructor video might not use V8 runtime.
So, in your script, when you want to test whether var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues(); works, how about disabling V8 runtime at the script editor as follows and testing it again?
Reference:
V8 Runtime Overview

For Loop for Event Object variables in Google App Script

Again newbee in app scripting I am trying to do the following
How to store array values in a variable for event object while on form submit i.e I need to get all values from form responses column in a sheet J to AF from a single row into a variable
something like var value = j,k,l,m,n,o(obviously as string without any spl chars like ,.etc)
instinctively there should be better way get all the values in a variable? tried some loop since the range starts from in-between columns(J-AF) of the sheet cant get it right
function onFormSubmit(e) {
Logger.log("%s", JSON.stringify(e));
//Get information from form and set as variables
var amount = [e.values[9],e.values[10],e.values[11],e.values[12],e.values[13],e.values[14],...e.values[31]];
Logger.log('Lets see if its here:'+ amount);
}
The e object from onFormSubmit(e) has the following structure:
{
authMode: {... },
namedValues:
{ 'Question 2': [ 'Answer 2' ],
Timestamp: [ '3/2/2020 9:48:53' ],
Question: [ 'Answer' ] },
range: { columnEnd: 3, columnStart: 1, rowEnd: 6, rowStart: 6 },
source: {},
triggerUid: '3xxxxxx825600xxx',
values: [ '3/2/2020 9:48:53', 'Answer', 'Answer 2' ]
}
As you can see, there are a couple of properties that can be useful for your case. (namedVales and values).
It is up to you to choose which to use.
An example:
function onFormSubmit(e) {
// Get values from response and put them in named variables
var amountDict = {
'Question 2' : e.namedValues['Question 2'],
'Question' : e.namedValues['Question'],
'Timestamp' : e.namedValues.Timestamp,
moreCols:'...', colAB: e.namedValues[10]
};
// Log them
console.log(amountDict);
// Get values from response and put them in unnamed variables
var amountList = [e.values[0],e.values[2], '...', e.values[3]];
// Log them
console.log(amountList);
}
Output:
Try this:
Don't forget to change the sheet name if needed.
And create the trigger.
function onFormSubmit(e) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
sh.getRange(sh.getLastRow()+1,10,1,e.values.length).setValues([e.values]);
}

how to map values to its corresponding row name

There is a collection of data which needs to be shown in the spreadsheet. I mean data will have values for each row(in this case Housing, CapitalRaised, SizePerSquare etc). How to plot below data in the spreadsheet in following format?
the data comes in this format
[
{
"Housing": "Before housing price",
"Price": 5,
"Rate": 0.75
},
{
"CapitalRaised": 5000,
"SizePerSquare": 12,
"Price": null,
"RatePerSquare": 1.25
},
{
"CapitalRaised": 6000,
"SizePerSquare": 24,
"Price": null,
"RatePerSquare": 1
},
{
"CapitalRaised": 7000,
"SizePerSquare": 24,
"Price": null,
"RatePerSquare": 0.75,
}
]
Here is the code
function plotData() {
var data =[
{
"Housing": "Before Capital Raised",
"Price": 5,
"Rate": 0.75
},
{
"CapitalRaised": 5000,
"SizePerSquare": 12,
"Price": null,
"RatePerSquare": 1.25
},
{
"CapitalRaised": 6000,
"SizePerSquare": 24,
"Price": null,
"RatePerSquare": 1
},
{
"AmountRaised": 7000,
"SizePerSquare": 24,
"Price": null,
"RatePerSquare": 0.75,
}
]
var activeSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "PlotData2";
var sheet = activeSheet.getSheetByName(sheetname);
var startRow = 4;
var range = "'" + sheetname + "'!B" + startRow;
}
Here is the link to spreadsheet
https://docs.google.com/spreadsheets/d/1tLNZv4F4lpBAnmHN5H0pBiirW4MVIfTexll9jPA03hI/edit#gid=1286090443
You want to achieve the following situation using Google Apps Script.
From:
var data = [
{"Housing":"Before housing price","Price":5,"Rate":0.75},
{"CapitalRaised":5000,"SizePerSquare":12,"Price":null,"RatePerSquare":1.25},
{"CapitalRaised":6000,"SizePerSquare":24,"Price":null,"RatePerSquare":1},
{"CapitalRaised":7000,"SizePerSquare":24,"Price":null,"RatePerSquare":0.75}
];
To:
The header titles of rows are constant.
If my understanding is correct, how about this answer? In this answer, from your question, I thought that the header titles of rows are constant. I used this situation.
Sample script:
Before you use the following scripts, please enable Sheets API at Advanced Google services. When the all column length of the array is not the same, the method of Spreadsheets.Values.update is easy to put the array to Spreadsheet. So I used Sheets API.
function plotData() {
var data = [
{"Housing":"Before housing price","Price":5,"Rate":0.75},
{"CapitalRaised":5000,"SizePerSquare":12,"Price":null,"RatePerSquare":1.25},
{"CapitalRaised":6000,"SizePerSquare":24,"Price":null,"RatePerSquare":1},
{"CapitalRaised":7000,"SizePerSquare":24,"Price":null,"RatePerSquare":0.75}
];
// Convert "data" to an array for putting to Spreadsheet.
var rowHeaders = ["Housing", "Price", "CapitalRaised", "RatePerSquare", "SizePerSquare"];
var values = data.reduce(function(ar, e, i) {
rowHeaders.forEach(function(g, n) {
if (!Array.isArray(ar[n])) ar[n] = [g];
ar[n][i + 1] = e[g];
});
return ar;
}, []);
// Put the converted array to Spreadsheet.
var activeSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheetname = "PlotData2";
var sheet = activeSheet.getSheetByName(sheetname);
var startRow = 4;
var range = "'" + sheetname + "'!B" + startRow;
Sheets.Spreadsheets.Values.update({values: values}, activeSheet.getId(), range, {valueInputOption: "USER_ENTERED"});
}
In above script, if there are the keys, which are not included in rowHeaders, in the keys of object of data, the values of the keys are not used. Please be careful this.
For example, Rate of the first element of data is not used.
Note:
In your data, it seems that the data of the data comes in this format is different from the data of Here is the code. "CapitalRaised": 7000, and "AmountRaised": 7000 are different. From your image of the expected result, I supposed that you want to use the data of the data comes in this format. If I misunderstood your goal, I apologize.
Reference:
Method: spreadsheets.values.update
If I misunderstood your question and this was not the direction you want, I apologize.

Google Apps Script Directory API to Write ALL User fields to a Google Sheet

Trying to write all users.organizations.name (title, primary, type, customType, etc) via GAS Directory API to a Google Sheet. Script below separates fullName, primaryEmail, & id into separate columns but writes organizations into a single column. I'd like to spilt organizations values up into separate columns.
function getAllUsers2(){
var values = [],
users = [],
userListQuery = {},
nextPageToken = '',
listObject = {
domain:'hhs1.com',
maxResults: 500,
projection: 'full',
customerId: 'my_customer'
},
i = 0;
do {
if (nextPageToken && nextPageToken !== '') {
listObject.pageToken = nextPageToken;
}
userListQuery = AdminDirectory.Users.list(listObject);
// if there are more users than fit in the query a nextPageToken is returned
nextPageToken = userListQuery.nextPageToken;
// Add the query results to the users array
users = users.concat(userListQuery.users);
} while (nextPageToken);
for (i = 0; i < 5; i += 1) {
values.push([users[i].name.fullName,
users[i].primaryEmail,
users[i].id,
users[i].organizations]);
}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetName = 'USERS2';
var header = ["Name", "Email","ID","Organizations"]
sheetExists(sheetName);
var sheet = ss.getSheetByName(sheetName);
sheet.getRange(1, 1, 1, header.length).setValues([header]); //Note: I had to add brackets around header for this to work. Was getting "Cannot convert Array to Object[][]" before I did so
sheet.getRange(2, 1, values.length, values[0].length).setValues(values);
Logger.log(values);
}
Here's what is added in column D after running:
partial product from script
Here is part of the USERS JSON Template for reference:
"organizations": [
{
"name": string,
"title": string,
"primary": boolean,
"type": string,
"customType": string,
"department": string,
"symbol": string,
"location": string,
"description": string,
"domain": string,
"costCenter": string
}
],
Thank you!

Understanding ES6 to ES5 code as transpiled by Babel

Babel transpiles the following ES6 code
test(){
var list = [ 1, 2, 3 ]
var [ a, , b ] = list
[ b, a ] = [ a, b ]
}
into this
function test() {
var list = [1, 2, 3];
var _list = list[(b, a)] = [a, b];
var _list2 = _slicedToArray(_list, 3);
var a = _list2[0];
var b = _list2[2];
}
I can't understand what exactly is happening with this line of code
var _list = list[(b, a)] = [a, b];
Specifically, I am clueless with list[(b, a)]. Any help is much appreciated?
The short answer is that
var [ a, , b ] = list
[ b, a ] = [ a, b ]
is the same as
var [ a, , b ] = list[ b, a ] = [ a, b ]
because automatic semicolon insertion does not apply to this situation. The same case applies to this ES5 example:
var list = [1, 2, 3]
var value = list
[1]
console.log(value);
If you run this code, it will log 2, not [1, 2, 3] because the index is applied to the previous line, rather than treated as an array on the following line.
Automatic semicolon insertion generally applies in cases where the parser encounters a syntax error, goes back a step, and tries after inserting a semicolon.
In both of these cases, the content of the following line is perfectly valid as part of the previous line, so it is treated as such with no automatic semicolon.
In your example, list[a, b] is parsed as accessing an index using the result of comma-operator operation, which will essentially evaluate to list[b]. The comma operator processes a list of expressions one at a time, and then finally evaluates to the result of the last expression.