Google Script not splitting array at comma - google-apps-script

var String = "One,Three,Eight";
var temp = String.split(",");
var test = (temp[1][0]);
Browser.msgBox(''+test+'', Browser.Buttons.OK_CANCEL);
I've searched high and wide for why this won't return:
"One" as (temp[0][0])
"Three" as (temp[1][0])
"Eight" as (temp[2][0])
They are only returning O, T and E respectively in the message box.
I want each word to be its own array index, is it something to do with a limit?
Thanks

You can test the code by using Logger.log().
function sandbox() {
var String = "One,Three,Eight";
var temp = String.split(",");
Logger.log(temp);
}
This will show you the following value for temp: [One, Three, Eight]
If you log the elements of the array:
temp[0] = 'One'
temp[1] = 'Three'
temp[2] = 'Eight'
If you use temp[1][0] you get the first letter from 'Three'.
Reference:
String.prototype.split()
Accessing array elements
String: Character Access

Related

Pulling PubMed data into Google Sheets

I'm looking for some help. I am trying to grab an author's publications from PubMed and populate the data into Google Sheets using Apps Script. I've gotten as far as the code below and am now stuck.
Basically, what I have done was first pull all the Pubmed IDs from a particular author whose name comes from the name of the sheet. Then I have tried creating a loop to go through each Pubmed ID JSON summary and pull each field I want. I have been able to pull the pub date. I had set it up with the idea that I would do a loop for each field of that PMID I want, store it in an array, and then return it to my sheet. However, I'm now stuck trying to get the second field - title - and all the subsequent fields (e.g. authors, last author, first author, etc.)
Any help would be greatly appreciated.
function IMPORTPMID(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var author = sheet.getSheetName();
var url = ("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=" + author + "[author]&retmode=json&retmax=1000");
var response = UrlFetchApp.fetch(url);
var AllAuthorPMID = JSON.parse(response.getContentText());
var xpath = "esearchresult/idlist";
var patharray = xpath.split("/");
for (var i = 0; i < patharray.length; i++) {
AllAuthorPMID = AllAuthorPMID[patharray[i]];
}
var PMID = AllAuthorPMID;
var PDparsearray = [PMID.length];
var titleparsearray = [PMID.length];
for (var x = 0; x < PMID.length; x++) {
var urlsum = ("https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&retmode=json&rettype=abstract&id=" + PMID[x]);
var ressum = UrlFetchApp.fetch(urlsum);
var contentsum = ressum.getContentText();
var jsonsum = JSON.parse(contentsum);
var PDpath = "result/" + PMID[x] + "/pubdate";
var titlepath = "result/" + PMID[x] + "/title";
var PDpatharray = PDpath.split("/");
var titlepatharray = titlepath.split("/");
for (var j = 0; j < PDpatharray.length; j++) {
var jsonsum = jsonsum[PDpatharray[j]];
}
PDparsearray[x] = jsonsum;
}
var tempArr = [];
for (var obj in AllAuthorPMID) {
tempArr.push([obj, AllAuthorPMID[obj], PDparsearray[obj]]);
}
return tempArr;
}
From a PubMed JSON response for a given PubMed ID, you should be able to determine the fieldnames (and paths to them) that you want to include in your summary report. Reading them all is simpler to implement if they are all at the same level, but if some are properties of a sub-field, you can still access them if you give the right path in your setup.
Consider the "source JSON":
[
{ "pubMedId": "1234",
"name": "Jay Sahn",
"publications": [
{ "pubId": "abcd",
"issn": "A1B2C3",
"title": "Dynamic JSON Parsing: A Journey into Madness",
"authors": [
{ "pubMedId": "1234" },
{ "pubMedId": "2345" }
]
},
{ "pubId": "efgh",
...
},
...
],
...
},
...
]
The pubId and issn fields would be at the same level, while the publications and authors would not.
You can retrieve both the pubMedId and publications fields (and others you desire) in the same loop by either 1) hard-coding the field access, or 2) writing code that parses a field path and supplying field paths.
Option 1 is likely to be faster, but much less flexible if you suddenly want to get a new field, since you have to remember how to write the code to access that field, along with where to insert it, etc. God save you if the API changes.
Option 2 is harder to get right, but once right, will (should) work for any field you (properly) specify. Getting a new field is as easy as writing the path to it in the relevant config variable. There are possibly libraries that will do this for you.
To convert the above into spreadsheet rows (one per pubMedId in the outer array, e.g. the IDs you queried their API for), consider this example code:
function foo() {
const sheet = /* get a sheet reference somehow */;
const resp = UrlFetchApp.fetch(...).getContentText();
const data = JSON.parse(resp);
// paths relative to the outermost field, which for the imaginary source is an array of "author" objects
const fields = ['pubMedId', 'name', 'publications/pubId', 'publications/title', 'publications/authors/pubMedId'];
const output = data.map(function (author) {
var row = fields.map(function (f) {
var desiredField = f.split('/').reduce(delve_, author);
return JSON.stringify(desiredField);
});
return row;
});
sheet.getRange(1, 1, output.length, output[0].length).setValues(output);
}
function delve_(parentObj, property, i, fullPath) {
// Dive into the given object to get the path. If the parent is an array, access its elements.
if (parentObj === undefined)
return;
// Simple case: parentObj is an Object, and property exists.
const child = parentObj[property];
if (child)
return child;
// Not a direct property / index, so perhaps a property on an object in an Array.
if (parentObj.constructor === Array)
return collate_(parentObj, fullPath.splice(i));
console.warn({message: "Unhandled case / missing property",
args: {parent: parentObj, prop: property, index: i, pathArray: fullPath}});
return; // property didn't exist, user error.
}
function collate_(arr, fields) {
// Obtain the given property from all elements of the array.
const results = arr.map(function (element) {
return fields.slice().reduce(delve_, element);
});
return results;
}
Executing this yields the following output in Stackdriver:
Obviously you probably want some different (aka real) fields, and probably have other ideas for how to report them, so I leave that portion up to the reader.
Anyone with improvements to the above is welcome to submit a PR.
Recommended Reading:
Array#reduce
Array#map
Array#splice
Array#slice
Internet references on parsing nested JSON. There are a lot.

Matching array contents to pre-entered data

I have an array, the contents of which are a subset of a list of names that come from a checkbox question in a google form. I need to email the people whose names are in the array, I suppose from a hard coded list (multi-dim array?). I cannot figure out how to perform the search/comparisons/whatever. Apparently I am supposes to use an object literal as in the code below:
var formNames = ["Name One", "Name Three"]; // one possibility for example
var objectMatchingNamesToEmails{
"Name One":"nameone#work.com",
"Name Two":"nametwo#work.com",
"Name Three":"namethree#work.com",
};
You could loop through the array:
var arrayOfEmails,arrayOfNames,L,thisEmail,thisName;
arrayOfNames = ["NameOne","NameTwo"];
arrayOfEmails = [];
L = arrayOfNames.length;//The number of names in the array
for (var i = 0;i<L;i++) {
thisName = arrayOfNames[i];
thisEmail = objectMatchingNamesToEmails[thisName];
arrayOfEmails.push(thisEmail);
};
Create an object literal:
var objectMatchingNamesToEmails;
objectMatchingNamesToEmails = {
"NameOne":"exampleOne#gmail.com",
"NameTwo":"exampleTwo#gmail.com",
"NameThree":"exampleThree#gmail.com",
};
Then after you get the name, the code can look up the correct email:
var userName,userEmail;
userName = code here to get user name;
userEmail = objectMatchingNamesToEmails[userName];
MailApp.sendEmail(userEmail,subject,body);

Google Sheet - Split Cell contents into rows and columns

Linked Google Sheet: https://docs.google.com/spreadsheets/d/1j2P2V0SCiE7QtK7kdCorpLfYQPFRghKMw4tuCYbqce0/edit?usp=sharing
I have a form that outputs all products and quantities into one cell:
"Product: NF900XC, Quantity: 3
Product: NF900, Quantity: 2
Product: N1930CB, Quantity: 2
Product: N2120, Quantity: 1
Product: NLPCR200, Quantity: 2
Product: N272, Quantity: 2"
I need each Product and Quantity on their own Line with its corresponding associated data collection.
I would like the contents (column O) split into their respective columns:
**Product** : **Quantity**
NF900 : 2
N1930CB : 2
N2120 : 1
NLPCR200: 2
N272 : 2
I have used SPLIT(text, delimiter, [split_around_each_character]) command for First Name Last Name but am unsure how to parse the rest of the text into their rows and columns in this situation.
I will also be functionally copying rows with the timestamp in
column A
Submission Date
that correspond to the Products:Quantity in cell O
I hope this makes sense.
Here is some code that I've tested and it works. The code removes all the words "Product: ", and replaces them with a comma. Then the code does something similar with the string "Quantity: ", replacing it with an empty string. Next it creates an array, and converts the array to a two dimensional array, so that the rows and columns can be written all in one action.
function convertData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Form Submission');
var data = sh.getRange("O2").getValue();
//Logger.log(data);
var firstProductRemoved = data.replace("Product: ", "");
//Logger.log(firstProductRemoved);
var allProductReplacedWithComma = firstProductRemoved.replace(/Product: /g,",");
//Logger.log(allProductReplacedWithComma);
var allQuantityReplacedWithNothing = allProductReplacedWithComma.replace(/Quantity: /g,"");
//Logger.log(allQuantityReplacedWithNothing);
var dataAsArray = allQuantityReplacedWithNothing.split(",");
var outerArray = [], innerArray = [];
var i=0;
for (i=0;i<dataAsArray.length;i+=2) {
innerArray = []; //reset every loop
innerArray.push(dataAsArray[i]);
innerArray.push(dataAsArray[i+1]);
outerArray.push(innerArray);
};
//Logger.log(outerArray);
var orderItemsSh = ss.getSheetByName('Order Items');
orderItemsSh.getRange(orderItemsSh.getLastRow()+1, 15,outerArray.length, 2).setValues(outerArray);
};
You might be interested in a more general solution, rather than one that needs to rely on knowing the specific phrased in the input strings.
This kvStringToArray() function will return an array that can be written directly to a spreadsheet use Range.setvalues(). It's pure JavaScript, so it can also be used outside of the Google Apps Script environment.
A RegExp is used to successively identify key:value pairs in the input data, and the function assumes that there are "rows" of information that can be identified by repeats of already-discovered "keys".
var formInput = "Product: NF900XC, Quantity: 3 Product: NF900, Quantity: 2 Product: N1930CB, Quantity: 2 Product: N2120, Quantity: 1 Product: NLPCR200, Quantity: 2 Product: N272, Quantity: 2";
snippet.log(
JSON.stringify(
kvStringToArray(formInput)));
/**
* Convert a given string of key:value pairs into a two-dimensional
* array, with keys as headers, and "items" as rows.
* From: http://stackoverflow.com/a/34847199/1677912
*
* #param {string} str A string containing key:value pairs.
*
* #returns {string[][]} A two-dimensional array of strings.
*/
function kvStringToArray( str ) {
var re = /\s*(\w*?)\s*:\s*([^,\s]*)/g, // See https://regex101.com/r/kM7gY1/1
arr, // array to capture key:value pairs
data = [], // array to return table
row = [], // array for building table rows
headers = []; // array of unique keys, for table header
// Use a RegEx to identify individual key:value pairs
while ((arr = re.exec(str)) !== null) {
var key = arr[1]; // $1 matches key
var value = arr[2]; // $2 matches value
// Check if we should start a new row
if (headers.indexOf(key) == 0) {
data.push(row);
row = [];
}
// Save this value in row
row.push(value);
// If this is the first time we've seen this key, add it to headers.
if (headers.indexOf(key) == -1) headers.push(key);
}
data.push(row); // save last row
data.unshift(headers); // add headers
return data;
}
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

How to create document from data array

I have FlexTable with chekBoxes in first cell of each row, when checkBox is true data from FlexTable's row is collected in variable. Now I need to create document with table that contains table with data from variable. I tried to store string's value in Hidden but it doesn't work and can't figure out how to realise it.
All my (although the code is not really my, code is almost half #Sergeinsas's) code is avaliable here: http://pastebin.com/aYmyA7N2, thankyou in advance.
There are a few errors in your code... widgets like hidden can only have string values and they can only return string values when you retrieve their values.
One possible and easy way to convert arrays to string (and back) is to use a combination of join() and split() , here is the modified code (relevant part only) that works.
// Storing checked rows
function check(e) {
var checkedArray = [];
var data = sh.getRange(1,1,lastrow,lastcol).getValues();
for(var n=0; n < data.length;++n){
if(e.parameter['check'+n]=='true'){
checkedArray.push(data[n].join(','));// convert data row array to string with comma separator
}
}
var hidden = app.getElementById('hidden');
hidden.setValue(checkedArray.join('|'));// convert array to string with | separator
return app;
}
function click(e) {
var hiddenVal = e.parameter.hidden.split('|');// e.parameter.hidden is a string, split back in an array of strings, each string should be splitted too to get the original array of arrays
var d = new Date();
var time = d.toLocaleTimeString();
var table = []
for(var n in hiddenVal){
table.push(hiddenVal[n].split(','));// reconstruction of a 2D array
}
DocumentApp.create('doc '+time).getBody().appendTable(table);// the table is in the document
}
Full code available here
EDIT : suggestion : if you put your headers in your spreadsheet you could retrieve them in your final table quite easily like this :
function check(e) {
var checkedArray = [];
var data = sh.getRange(1,1,lastrow,lastcol).getValues();
checkedArray.push(data[0].join(','));// if you have headers in your spreadsheet, you could add headers by default
for(var n=0; n < data.length;++n){
if(e.parameter['check'+n]=='true'){
checkedArray.push(data[n].join(','));
}
}
You could also use data[0] in the doGet function to build the header of your UI, I think this would make your code more easy to maintain without hardcoding of data.... but this is only a suggestion ;-)

Query variable arrays

How do I use an array variable as a query argument instead of the literal array itself?
For example, the documentation mentions the following:
var result = db.query({name: db.anyOf(['fred', 'barney', 'mark']});
But instead, I wish to do this:
var myTeam = ["fred","barney","mark"];
var result = db.query({name: db.anyOf(myTeam)});
So far, I have not been successful.
What am I missing?
Nothing. Your code works fine for me (besides the missing parenthesis typo).
function scriptdbTest() {
var db = ScriptDb.getMyDb();
db.save({name:'fred', age:40}); //just to get one result on my test script
var myTeam = ["fred","barney","mark"];
var result = db.query({name: db.anyOf(myTeam)});
while( result.hasNext() )
Logger.log(result.next().toJson());
}