Going to start with, I am extremely new to this. We are using a google sheet as different forms of sorts where we want to capture data in a single source. I am trying to avoid having to individually map each cell. below is the start of the 100 fields i'll have to capture. I am getting the error that my data of 3 doesn't match my columns of 10. Is there way to lay out different ranges and get them to all push to one row?
//Input Values
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getActiveSheet();
var datasheet = ss.getSheetByName("Data");
var values = [[formS.getRange("B1:B4").getValues(),
formS.getRange("D2:D4").getValues(),
formS.getRange("F2").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1,10).setValues(values)
I think that in your script, values for putting to Spreadsheet is required to be 2 dimensional array. But, formS.getRange("B1:B4").getValues() and formS.getRange("D2:D4").getValues() are 2 dimensional array and formS.getRange("F2").getValue() is not array. I think that this is the reason of your issue.
In order to put those values to one row of the sheet Data, how about the following modification?
Modified script:
From:
var values = [[formS.getRange("B1:B4").getValues(),
formS.getRange("D2:D4").getValues(),
formS.getRange("F2").getValue()]];
datasheet.getRange(datasheet.getLastRow()+1, 1, 1,10).setValues(values)
To:
var values = [[
...formS.getRange("B1:B4").getValues().flat(),
...formS.getRange("D2:D4").getValues().flat(),
formS.getRange("F2").getValue()
]];
datasheet.getRange(datasheet.getLastRow() + 1, 1, 1, values[0].length).setValues(values);
By this modification, values is like [ [ 'B1', 'B2', 'B3', 'B4', 'D2', 'D3', 'D4', 'F2' ] ]. Each value of the array is the a1Notation of the cell. And, this is put to the next row of the last row of sheet Data.
In this case, the length of values[0] is 8. It's not 10. So I used values[0].length.
References:
getValues()
setValues(values)
flat()
Spread syntax (...)
Related
From #soMario's answer to a certain question, getting the data of specific column can be done like this:
The following will get columns b,d,f,g from the range A:G.
const rawData = ss.getRange("A:G").getValues().map(([,b,,d,,f,g]) => [b,d,f,g]);
How can I get the same, but using getRange(2,1,sheet.getLastRow(),7) instead?
Thank you!
when you getValues() of any range, it is stored as one array of many arrays, which looks like this, structurally for the range A1:G3 :
[ [ , , , , , , ], [ , , , , , , ], [ , , , , , , ] ]
when you want a specific "column", say column 4 of that range, in javascript/appscript it means you want the 4th element of each of the arrays in the array. When counting from 0, the 4th element is the 3rd "index".
When using the mapping function, each of the elements of the array being mapped is assigned any variable you want. It is common to use "e". So for your specific case you would want to do this
const rawData = ss.getRange("A:G").map(e=>[ e[1], e[3], e[5], e[6] ]);
1,3,5 and 6 being the "indices" of columns B,D,F and G when starting to count with A as 0.
However, it's likely that you'll want a filter on your data as well to only return rows where there are values in column A. If that guess is correct, you can apply a filter before your map like this:
const rawData = ss.getRange("A:G").filter(e=>e[0]).map(e=>[ e[1], e[3], e[5], e[6] ]);
I have a JSON file and I want to output the data to a table in Google Sheet. Since the data order is not guaranteed, I need to search for the corresponding row for the second and subsequent data set (Tuesday data from my example below).
Apart from looping thru the first column, is there a faster or more elegant way to do this?
function test(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var SSheet=ss.getActiveSheet();
var data={Monday:{Apple:2, Orange:3}, Tuesday:{Orange:4, Apple:5}};//intentionally swap the key
var row=2;
var column=2;
var bWriteHeader=true;
for (const g in data){
SSheet.getRange(1,column).setValue(g)
row=2;
for (const k in data[g]){
if (bWriteHeader){
SSheet.getRange(row,1).setValue(k);
SSheet.getRange(row,column).setValue(data[g][k]);
row++;
}else{
//Search for the corresponding row--- how to do it elegantly, apart from looping thru first column?
SSheet.getRange(row,column).setValue(data[g][k]);
}
}
bWriteHeader=false;
column++;
}
}
Desired output:
Monday Tuesday
Apple 2 5
Orange 3 4
I believe your goal as follows.
You want to reduce the process cost of your script.
Although I'm not sure whether my proposed method is more elegant way, in this answer, how about the following flow?
Retrieve a row header.
Retrieve a column header.
Create an array using the row header and column header.
Put the values to the active sheet.
In this flow, after the array was created, the array is put to the Spreadsheet. By this, I thought that the process cost will be lower than your script that setValue is used in a loop. Ref When this flow is reflected to the script, it becomes as follows.
Sample script:
function myFunction() {
var data = { Monday: { Apple: 2, Orange: 3 }, Tuesday: { Orange: 4, Apple: 5 } };//intentionally swap the key
// 1. Retrieve a row header.
var rowHeader = Object.keys(data).sort();
// 2. Retrieve a column header.
var colHeader = Object.keys(data[rowHeader[0]]).sort();
// 3. Create an array using the row header and column header.
var values = rowHeader.reduce((ar, k) => ar.concat([[k, ...colHeader.map(l => data[k][l])]]), [["", ...colHeader]]);
var res = values[0].map((_, i) => values.map(r => r[i]));
// 4. Put the values to the active sheet.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.getRange(1, 1, res.length, res[0].length).setValues(res);
}
Testing:
var data = { Monday: { Apple: 2, Orange: 3 }, Tuesday: { Orange: 4, Apple: 5 } };//intentionally swap the key
// 1. Retrieve a row header.
var rowHeader = Object.keys(data).sort();
// 2. Retrieve a column header.
var colHeader = Object.keys(data[rowHeader[0]]).sort();
// 3. Create an array using the row header and column header.
var values = rowHeader.reduce((ar, k) => ar.concat([[k, ...colHeader.map(l => data[k][l])]]), [["", ...colHeader]]);
var res = values[0].map((_, i) => values.map(r => r[i]));
console.log(res)
References:
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
map()
Object.keys()
Object.values()
I have two sheets. Test Data has 3-4k entries of many columns of data and Order Changes has no data at all. I would like to search two specific columns on Test Data, a column of names and a column of yes or no. If column two of Test Data contains a 'yes' in the cell then the name of that person would be placed into a cell on order changes.
This is what I have so far:
function isThreshold(){
var data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test Data");
var cdata = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Order Changes");
var lc = data.getLastColumn();
var lookUp = data.getRange(1,6,3,2).getValues();
lookUp.forEach(var info in lookUp){
}
Logger.log(lookUp);
}
I probably shouldn't loop through that many entries but I don't know of any other way. Should I combine the forEach loop with an if loop to get the desired result or use some other method?
I believe your goal as follows.
You want to retrieve the values from the cells "F1:G" of sheet "Test Data".
You want to search yes from the column "G" and when the column "G" is yes, you want to put the value of the column "F" to the sheet "Order Changes".
Modification points:
SpreadsheetApp.getActiveSpreadsheet() can be declared one time.
In this case, you can retrieve the values from the range of "F1:G" + data.getLastRow() of "Test Data", and create the array for putting to the sheet "Order Changes", and put it.
When above points are reflected to your script, it becomes as follows.
Modified script:
function isThreshold(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getSheetByName("Test Data");
var cdata = ss.getSheetByName("Order Changes");
var valuesOfTestData = data.getRange("F1:G" + data.getLastRow()).getValues();
var valuesForOrderChanges = valuesOfTestData.reduce((ar, [f, g]) => {
if (g.toLowerCase() == "yes") ar.push([f]);
return ar;
}, []);
if (valuesForOrderChanges.length > 0) {
cdata.getRange(1, 1, valuesForOrderChanges.length, valuesForOrderChanges[0].length).setValues(valuesForOrderChanges);
// or cdata.getRange(cdata.getLastRow() + 1, 1, valuesForOrderChanges.length, valuesForOrderChanges[0].length).setValues(valuesForOrderChanges);
}
}
In this modified script, from your question, it supposes that the columns "F" and "G" are the value of name and yes or no.
References:
getRange(a1Notation) of Class Sheet
reduce()
I get the following string returned by the CBOE options api:
{u'inputs': {u'stock_price_max': 50.0, u'high_low_stock_max': None, u'time_frame': u'middle', u'hv30_max': None, u'high_low_stock_min': None, u'symbols': None, u'hv30_min': None, u'low_strike': 3.0, u'high_strike': 4.0, u'industry_codes': None, u'spread_ask_price_max': None, u'stock_price_min': 10.0}, u'output': [{u'stock_price': 43.2, u'stock_hi_lo_percent': 72.9651, u'symbol': u'EWZ', u'industry_code': 55501010, u'max_gain': 0.52, u'high_strike_otm_percent': 0.463, u'low_strike_otm_percent': 2.7778, u'spread_ask': 0.48, u'spread': u'43/42 Put', u'expiry': u'2019-04-18', u'max_gain_to_spread_ask_percent': 108.3333, u'hv30': 27.3836}, {u'stock_price': 41.37, u'stock_hi_lo_percent': 21.7957, u'symbol': u'FXI', u'industry_code': 55501010, u'max_gain': 0.26, u'high_strike_otm_percent': 0.8944, u'low_strike_otm_percent': 2.103, u'spread_ask': 0.24, u'spread': u'41/40.5 Put', u'expiry': u'2019-05-17', u'max_gain_to_spread_ask_percent': 108.3333, u'hv30': 20.2925}
I want to loop through it and place elements into cells in a Google spreadsheet. I have this code:
function myFunction() {
var response = UrlFetchApp.fetch(endpoint);
var data = response.getContentText();
sheet.getRange("A8").setValue(data);
}
This puts the entire string into cell A8.
I have tried to loop through the string with
for (i = 0; i < jsonlen; i++) {
sheet.getRange("A:A").setValaue(data['output']['symbol']);
}
This returns "undefined". So problems are:
1) how can I extract the elements I need form the "output" part of the string
2) put the symbols into A3, A4 etc then stock_price into B3, B4 etc
3) how to identify the length of the string in order to make the loop work correctly
until the string has been entirely looped over?
Many thanks!
You want to retrieve the values of symbol and stock_price the property of output and want to put them to the columns "A" and "B" of the active Spreadsheet, respectively.
You want to put the values from the row 3.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modification points:
the property of output has an array. And symbol and stock_price are in the array.
So at first, it is required to prepare the values for putting to Spreadsheet.
Modified script:
function myFunction() {
var response = UrlFetchApp.fetch(endpoint);
var data = JSON.parse(response.getContentText());
var values = data.output.map(function(e) {return [e.symbol, e.stock_price]});
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(3, 1, values.length, values[0].length).setValues(values);
}
In this case, the values are put to the row 3 of column "A" and "B" on the active sheet.
Note:
In your case, if data is actually the string value of {u'inputs': {###}, u'outputs': [###]} which has the unicode literal of python 2, u is required to be replaced. So in this case, please put data = JSON.parse(data.replace(/u\'|\'/g, "\"").replace(/None/g, "null")); before var values = data.output.map(function(e) {return [e.symbol, e.stock_price]});. Please be careful this.
References:
map()
getRange(row, column, numRows, numColumns)
setValues(values)
If I misunderstood your question and this was not the result you want, I apologize.
I help maintain a Google spreadsheet where new data is added via a HTML form.
When it comes to add new data the insertion point of the new data depends on one of the form fields (Application Received date).
The script finds where in the sheet the data should be inserted and does 3 things:
Inserts a blank row at the correct location
Copies the row above (so formulas and conditional formatting are copied)
Replaces the data in the cells from the copy with the values entered into the form
The issue is cells A to I are value based (populated from the form) and so are cells M to O, but cells J,K,L are calculations based on some cells in A to I.
This means I have to make 2 calls to getRange/setValues and sometimes the second call (the call to set cells M,N,O does not work. The result is a new row created with the correct data in cells A to I (and thus J,K,L) but cells M,N,O stay as whatever is in those cells in the row above.
Here is the relevant code.
// Assign object data for cells A to I
var newvalues = [
[ username, applyDate, maritalStatus, sponsorApprovalDate, processingOffice, inProcessDate, extraDocsRequestedDate, nonVisaExempt, decisionMadeDate ]
];
// Set cells A to I with data from form
sheet.getRange('A' + startingRowIndex + ':I' + startingRowIndex).setValues(newvalues);
// Now assign object data for cells M to O
newvalues = [
[ coprReceivedDate, location, notes ]
];
// Set cells M to O with data from form
sheet.getRange('M' + startingRowIndex + ':O' + startingRowIndex).setValues(newvalues);
As stated above the second sheet.getRange('...').SetValues() call fails to set the values.
Any ideas?
Instead of completely recalculating the locations of your output ranges, you could get an "anchor" point at the start of the row, then use the Range.offset() method to define additional ranges relative to the anchor.
// Assign object data for cells A to I
var newvalues = [
[ username, applyDate, maritalStatus, sponsorApprovalDate, processingOffice, inProcessDate, extraDocsRequestedDate, nonVisaExempt, decisionMadeDate ]
];
// Get range "anchor" for data from form
var newRow = sheet.getRange('A' + startingRowIndex );
// Set cells A to I with data from form
newRow.offset(0,0,newvalues.length,newvalues[0].length).setValues(newvalues);
// Now assign object data for cells M to O
newvalues = [
[ coprReceivedDate, location, notes ]
];
// Set cells M to O with data from form
newRow.offset(0,13,newvalues.length,newvalues[0].length).setValues(newvalues);