I have defined a couple of named ranges in my sheet following the naming schema fieldName1, fieldName2, fieldName3 etc. They are located in different places in my sheet.
I am trying to load those fieldName(s) into an array now, but I encounter an issue, as
getRange("fieldName1").getValue() is expecting a "String" of fieldName rather than a getRange(fieldName[c]).getValue() code.
Here is what I have so far:
const noFields = wsS.getRange("noFields").getValue()
var fieldName = []
for (var c = 1; c <= noFields; ++c) {
fieldName[c] = wsS.getRange(fieldName[c]).getValue()
}
noFields is a variable of how many fieldNameX named ranges I have to load, as I didn't think of a better way to load them still.
Please help, thank you.
So you want all the named ranges that start with "fieldName", and want to get all their values as an array.
You can get all the named ranges from the spreadsheet using .getActive(). However, they will not be sorted as expected.
If you simply want an array with all the values from named ranges, do:
var ss = SpreadsheetApp.getActive()
var values = ss.getNamedRanges()
.map(namedRange => namedRange.getRange().getValue())
If you only want the values from those ranges following the naming schema, do:
const PREFIX = "fieldName" // case sensitive
var values = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
.map(namedRange => namedRange.getRange().getValue())
You might also want to sort the named ranges by the number after "fieldName", just in case they were entered in different order, right? But that complicates things a little since the names are strings.
One way of sorting the array is by constructing an associative array with the range names:
const PREFIX = "fieldName" // case sensitive
var ss = SpreadsheetApp.getActive()
var namedRanges = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
var associativeArray = []
for (var namedRange of namedRanges) {
associativeArray[namedRange.getName().substring(PREFIX.length)] =
namedRange.getRange().getValue()
}
var values = associativeArray.flat()
Or with a custom comparator:
const PREFIX = "fieldName" // case sensitive
var ss = SpreadsheetApp.getActive()
var values = ss.getNamedRanges()
.filter(namedRange => namedRange.getName().startsWith(PREFIX))
.map(namedRange => [
Number(namedRange.getName().substring(PREFIX.length)),
namedRange.getRange().getValue() ])
.sort((itemA,itemB) => itemA[0] - itemB[0])
.map(item => item[1])
In any case, you will end up with an array of values that you can use.
Get all named ranges in to an array
SpreadsheetApp.getActive().getNamedRanges();
Class namedRanges
function namedRangesInAnArray() {
const ss = SpreadsheetApp.getActive();
const names = ['r1','r2'.....];//array of names to get
return SpreadsheetApp.getActive().getNamedRanges().filter(r => ~names.indexOf(r.getName()));
}
Related
I got this these values.
And I want to have this result.
So I made the following test code and tried it to the first cell.
function test2() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("richText3");
const range1 = sheet.getRange("A1");
const text1 = range1.getValue();
Logger.log(text1);
const re = new RegExp(/\([ a-zA-Z\/']*\)\?/dg);
const redBold = SpreadsheetApp.newTextStyle().setBold(true).setForegroundColor('red').build();
let array;
while ((array = re.exec(text1)) !== null) {
const [start, end] = array.indices[0];
const richTxtValBlder = SpreadsheetApp.newRichTextValue()
.setText(text1)
.setTextStyle(start, end, redBold)
.build();
range1.setRichTextValue(richTxtValBlder);
}
}
After first try, I got this result.
I checked the Reference Document again and I found this comment.
setText(text) : Sets the text for this value and clears any existing text style.
When creating a new Rich Text value, this should be called before setTextStyle()
I found that I should call .setText() once and call .setTextStyle() multiple times.
But the problem is .setTextStyle() should be called programmatically according to the number of patterns in each cell and I cannot find how to do it programmatically.
Each cell may have 0 to 10 patterns and I don't want to make 10 different richTExtValueBuilder which only differ in the number of .setTextStyle() calls.
Do you have any different ideas ?
Modification points:
In your script, only cell "A1" is used, and also the 1st match is used. I thought that this might be the reason for your issue.
In order to achieve your goal, I retrieve the values from column "A". And also, I use matchAll instead of exec.
When these points are reflected in your script, how about the following modification?
Modified script:
function test2() {
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("richText3");
const range1 = sheet.getRange("A1:A" + sheet.getLastRow());
const re = new RegExp(/\([ a-zA-Z\/']*\)\?/g);
const redBold = SpreadsheetApp.newTextStyle().setBold(true).setForegroundColor('red').build();
const richTextValues = range1.getRichTextValues();
const res = richTextValues.map(([a]) => {
const array = [...a.getText().matchAll(re)];
if (array) {
const temp = a.copy();
array.forEach(a => temp.setTextStyle(a.index, a.index + a[0].length, redBold));
return [temp.build()];
}
return [a];
});
range1.setRichTextValues(res);
}
Testing:
When this script is run, the following result is obtained.
From:
To:
References:
map()
setRichTextValues(values)
It looks like you need to call .setText() once, .setTextStyle() multiple times, and .build() once, e.g. change your while loop. Untested code:
let richTxtValBlder = SpreadsheetApp.newRichTextValue().setText(text1);
while ((array = re.exec(text1)) !== null) {
const [start, end] = array.indices[0];
richTxtValBlder = richTxtValBlder.setTextStyle(start, end, redBold);
}
richTxtValBlder = richTxtValBlder.build();
range1.setRichTextValue(richTxtValBlder);
I am using google sheets quite a lot, but now I am trying to use google apps script to get and update dynamic data retrieved from formulas into a static table.
So, I have a sheet called 'dynamique', with formulas retrieving, filtering and sorting data from other spreadsheets.
I want to be able to work on this data, so I am trying to create a button which would copy all the values from the 'dynamique' sheet into another sheet called 'statique'. That is, I want a formula which would check if the values from the column C of the 'dynamique' sheet are in the column C of the 'statique' sheet. And if the values aren't there, I want the script to copy them. (columns A and B are empty)
I've managed to get my script to work for one column, but now, I want to copy the whole line.
For example, if the value in dynamique!C10 can't be found in statique!C:C, my script writes the value of dynamique!C10 in the first empty cell of the column statique!C:C. But I want it to write dynamique!C10:J10 into my destination sheet (say it's going to be maybe statique!C8:J8).
Here is my code, working for only one cell.
function dynamicToStatic() {
var dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
var staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
var dynLength = dynSheet.getRange("C1:C").getValues().filter(String).length;//.getLastRow();
var staLength = staSheet.getRange("C1:C").getValues().filter(String).length;
var staRange = staSheet.getRange(6,3,staLength-1);
var staValues = staRange.getValues();
var rangeToCheck = dynSheet.getRange(6,3,dynLength-1,8);
var valuesToCheck = rangeToCheck.getValues();
var numRows = rangeToCheck.getNumRows();
var staNumRows = staRange.getNumRows();
for (i = 0; i<= numRows; i++) {
var row = valuesToCheck[i];
var index = ArrayLib.indexOf(staValues , -1 , row);
if (index == -1) {
//if (staValues.indexOf(row) != -1) {
staSheet.getRange(i+6,3,1,8).setValues(row);
}
}
var timestamp = new Date();
staSheet.getRange(4,3).setValue('List updated on the: '+timestamp);
}
Now I can't manage to retrieve the whole line of the array, so as to be able to copy it using range.setValues(). I always get error messages.
Any help would be more than appreciated...
function gettingFullRows() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const shsr=2;//data startrow
const vA=sh.getRange(shsr,1,sh.getLastRow()-shsr+1,sh.getLastColumn()).getValues();
let html='';
vA.forEach((r,i)=>{
html+=Utilities.formatString('<br />Row:%s is %s',i+shsr,r.join(','));
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Row");
}
So i did some re-writing to your code and made some comments in there. I hope this will make some things clear.
Array's are 0 indexed. So if the value is NOT found in the .indexOf then it would return -1. Also (for speed) i first push all the result to a array and then set the array in one "action" this saves a lot of time. The calls to and from a sheet takes the most time.
For the conversion to a 1d array i used spread operator
See this link for difference in const / var / let
The timestamp string i updated with the use of Template literals
If you have some questions, shoot! (also i did not test this ofcourse)
function dynamicToStatic() {
const dynSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("dynamique");
const staSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("statique");
const dynValues = dynSheet.getRange(1,3,dynSheet.getLastRow(),8).getValues();
//This is a 2d array
const staRaw = staSheet.getRange(6, 3, staSheet.getLastRow()).getValues();
//Convert to 1d array, for the indexoff later on, this way it is easier.
const staValues = [].concat(...staRaw);
//to store the output, as a 2d array, inside the if you see i push it in as array so you have the 2d array for the setValues.
const output = [];
for (let i = 0; i < dynValues.length; i++){
//i = the index of the array (row) inside the array of rows, the 0 would be the values of column C.
if (staValues.indexOf(dynValues[i][0]) >= 0){
output.push([dynValues[i]]);
}
}
//Start by the lastrow + 1, column C(3), ouput is a array of arrays(rows), then get the [0].lengt for the columns inside the row array.
staSheet.getRange(staSheet.getLastRow()+1, 3, output.length, output[0].lenght).setValues(output);
const timestamp = new Date();
staSheet.getRange(4,3).setValue(`List updated on the: ${timestamp}`);
}
I am trying to post all entries in a 1d array to a column in a google sheets. The array is the product of filtering two larger arrays and returning the names that do not appear on both lists.
below is an example of the generated array.
unPub = [fake name, test1, test2, test3]
Here is the code I have written so far:
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1
if (unPub.length > 0){
unPub.forEach(e => allergy.getRange(sRow,1).setValue(e));
}
}
I have tried a for loop to iterate over the list as well as forEach and still only get the last entry of the unPub array to post in the defined range.
How can I get each element in the array to post to the column starting at sRow?
Explanation:
You don't need a loop to set values to the sheet. In fact it is not recommended, see best practices.
You need the following two steps:
transform your row array into a column array:
unPub=unPub.map(v=>[v]);
because you want to set the data into a column.
remove the forEach loop and directly pass the values with a single line:
allergy.getRange(sRow,1,unPub.length,1).setValues(unPub);
Solution:
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1;
unPub=unPub.map(v=>[v]);
allergy.getRange(sRow,1,unPub.length,1).setValues(unPub);
}
Issue with your approach:
Besides performance issues which I described in the explanation section, your forEach loop does not work because you overwrite every value on the same cell. If you see, this part allergy.getRange(sRow,1) does not change in the for loop, given that sRow is constant.
If you want your approach to work, then you need to introduce an iterator i in the forEach loop and use that to iterate through the cells:
unPub.forEach((e,i) => allergy.getRange(sRow+i,1).setValue(e));
function unPublished(){
const q3 = SpreadsheetApp.openById("1111111111");
const packAllergies = q3.getSheetByName("PACK_ALLERGIES");
const packSrch = packAllergies.getRange("D5:D" + packAllergies.getLastRow()).getValues().flat();
const allergyNames = allergy.getRange("A2:A" + allergy.getLastRow()).getValues().flat();
var unPub = (packSrch.filter(e => !allergyNames.includes(e)));
var sRow = allergy.getLastRow()+1
if (unPub.length > 0){
unPub.forEach((e,i) => allergy.getRange(sRow+i,1).setValue(e));
}
}
but I really recommend you the first approach I mentioned.
I am trying to pull a range of names from a Google sheet and place it into a Google Doc.In the spreadsheet, the last names("lastNames") come before the first names ("firstNames"), and both are in separate columns. I am trying to place the first and last names together into my doc with the first names first.
I used a for loop to put the first and last names together into an array ("fullNames"), and that part works just fine. When I used Logger.log, all the first names and last names are together in an array, with each full name separated by a common, just the way I wanted them to be.
What I can't figure out how to do is actually insert this new array into the body of the document. I am using the appendTable method, but every time I try to I get the following error: "The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable."
What changes do I have to make to my code to actually place my new array into my google doc?
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var lastNames = ss.getSheetByName("Campbell").getRange(2, 2, 18).getValues();
var firstNames = ss.getSheetByName("Campbell").getRange(2, 3, 18).getValues();
//Logger.log(firstNames);
var fullNames = [];
for(var i = 0; i < firstNames.length; i++){
var nameConcat = firstNames[i] + " " + lastNames[i]
fullNames.push(nameConcat);
}
//Logger.log(fullNames);
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
Modification points:
I think that there 2 reasons in your issue.
Values retrieved by getValues() is 2 dimensional array.
data of appendTable(data) is required to be 2 dimensional array.
In your script, fullNames is 1 dimensional array. By this, such error occurs.
In your script, the values are retrieved 2 columns using 2 getValues(). In this case, the cost will become a bit high. You can retrieve the values using one getValues().
When these points are reflected to your script, it becomes as follows.
Modified script:
function namePusher() {
var ss = SpreadsheetApp.openById("1CHvnejDrrb9W5txeXVMXxBoVjLpvWSi40ehZkGZYjaY");
var values = ss.getSheetByName("Campbell").getRange("B2:C19").getValues(); // Modified
var fullNames = [];
for(var i = 0; i < values.length; i++){ // Modified
var nameConcat = [values[i][1] + " " + values[i][0]]; // Modified
fullNames.push(nameConcat);
}
var doc = DocumentApp.getActiveDocument().getBody();
doc.appendTable(fullNames);
}
References:
getValues()
appendTable(cells)
One simple way to fix your code is by replacing
fullNames.push(nameConcat);
by
fullNames.push([nameConcat]);
The problem with your script is that fullNames is an Array of strings but your should pass an Array of Arrays of strings (or objects that might be coerced to strings).
Basic demo
var data = [
['A','B','C'],
[1, 'Apple','Red'],
[2, 'Banana','Yellow']
];
function myFunction() {
const doc = DocumentApp.getActiveDocument();
const body = doc.getBody();
body.appendTable(data);
}
As mentioned on Tanaike's answer there are other "improvement opportunities"
Reduce the number of calls to the Google Apps Script Classes and Methods
Use better ways to manage Arrays and to concatenate strings.
I would like to create a filter for a column in a spreadsheet, then retrieve the list of default criteria values created for the filter. I believe that my code returns a Filter object without any values for it.
function TestFilter(){
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
sheet.getRange(1, 2, sheet.getMaxRows(), 1).createFilter();
var filter = sheet.getFilter();
var output = filter.getColumnFilterCriteria(2).getCriteriaValues();
return output;
}
You can use the following functions for this:
getHiddenValues()
Returns the values to hide.
getVisibleValues()
Returns the values to show.
In case your filter is set to hide all of the possible values, you will obtain what you desire by using the function getHiddenValues().
However, this will not be possible if your filter is only hiding a subset of your values. For that case, you could use a Google Apps Script function such as the following below to obtain the distinct values:
function getDistinctValues(range) {
var values = range.getValues();
var unique = {};
for (var i=0; i<values.length; i++) {
for (var j=0; j<values[i].length; j++) {
var key = values[i][j];
if (key !== null && key !== undefined && key !== '')
unique[key] = true;
}
}
return Object.keys(unique);
}
The usage of it would be, in case you were attempting to obtain the distinct values on your A column:
var distinctValues = getDistinctValues(sheet.getRange("A2:A"));
Note that this function will return the values as Strings. In case you want to obtain the actual numeric value instead of a String, you can parse the values simply by using the following code:
var distinctValues = getDistinctValues(sheet.getRange("A2:A")).map(parseFloat);
I believe there is a bug with 2 out of 3 of these functions, by using something like this:
var filter = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sheetName").getFilter();
var criteriaValues = filter.getColumnFilterCriteria(9).getCriteriaValues();
Logger.log("criteria Values length " + criteriaValues.length);
Logger.log(criteriaValues);
var visibleValues = filter.getColumnFilterCriteria(9).getVisibleValues();
Logger.log("visible Values length " + visibleValues.length);
Logger.log(visibleValues);
var hiddenValues = filter.getColumnFilterCriteria(9).getHiddenValues();
Logger.log("hidden Values length " + hiddenValues.length);
Logger.log(hiddenValues);
and setting a filter on column I (9th from the left) regardless of how many or which values I filter by, I only ever see the values that I've hidden from the column, the criteriaValues and visibleValues arrays are always empty, while hiddenValues always shows correctly the values that are filtered out.
If someone could double check this and confirm it would be great, otherwise, maybe I'm just doing something really silly, in which case please let me know as well :).
This is created based on https://developers.google.com/apps-script/reference/spreadsheet/filter-criteria.html
Blockquote