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.
Related
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);
Array filtering not working as expected:
function getTest( myArray ) {
keySetTest = new Set('B-L002');
var t2= myArray[2][0];
var myResult = myArray.filter(dataRow =>keySetTest.has(dataRow[0]));
return myResult;
};
In the debugger it looks like this:
Why is myResult empty?
As can be seen from variable t2 it should contain at least the entry myArray[2],right?
I am using this logic almost identical in another context and it works fine.
About your question of Why is myResult empty?, when I saw your script, it seems that keySetTest = new Set('B-L002'); is required to be modified. Because I think that in your situation, when keySetTest = new Set('B-L002'); is used, each character is split like [ 'B', '-', 'L', '0', '2' ]. I thought that this might be the reason for your current issue.
When your script is modified, how about the following modification?
From:
keySetTest = new Set('B-L002');
To:
var keySetTest = new Set(['B-L002']);
Testing:
When the modified script is tested using a sample value, it becomes as follows.
var myArray = [["B-L002", "b1", "c1"], ["a2", "b2", "c2"], ["B-L002", "b3", "c3"]];
var keySetTest = new Set(['B-L002']);
var myResult = myArray.filter(dataRow => keySetTest.has(dataRow[0]));
console.log(myResult)
Note:
In this sample, I guessed your sample value of myArray. When you use this modified script in your actual situation, when your unexpected result is obtained, please provide the sample value of myArray and your sample expected value. By this, I would like to confirm it.
In your situation, the following modified script might be able to also obtain the same result.
var myArray = [["B-L002", "b1", "c1"], ["a2", "b2", "c2"], ["B-L002", "b3", "c3"]];
var myResult = myArray.filter(dataRow => dataRow[0] == "B-L002");
console.log(myResult)
Reference:
Set
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
I would like to store each row of data as a separate item in an array. This is the code I have so far:
function data() {
var responses = ss.getSheetByName('Form Responses 263')
var data = responses.getRange(2, 2, responses.getLastRow()-1, responses.getLastColumn()-1).getValues()
var n = []
data.forEach(function(row) {
n.push(row)
Logger.log(row)
Logger.log(n)
})
}
At the moment, this prints each rows contents to the Logger. How would I go about storing each row's data into var n? I have used:
n.push(row)
but this just adds everything from the row, not just the data.
Source row
I would like my array to look like this:
[1 3 4],[2 3 4], [2 4 4 6]
This was the final solution to the issue as answered by #Tanaike in comments:
function data() {
var fr = ...
var data = fr.getRange(2, 2, fr.getLastRow()-1, fr.getLastColumn())
.getValues().map(function(row) {
return row.filter(String)
})
Logger.log(data)
}
It uses the String class constructor as a predicate to remove empty indexes from the source rows.
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.