Map 3D array to cells in Google Sheet - Apps Scirpt - javascript

Hey I am trying to place arrays of data into a Google Sheet. I've got a list of dimensions which I am mapping through a function calling an external API to get numerical values for each dimension. I then want to input these into my spreadsheet - however I am unsure how to do this. I would also like to input the specific Date into a column as well. So ideal output would be column A = date, column B = dimensions, column C = numerical value.
function function() {
var dimensions = ["CN","IN","NZ","US","UK","MY","SG","JP","KR","DE"];
var counts = dimensions.map(dim => [getValue(dim)]);
}
function getValue(dim) {
// ive removed the detail for the API call
var response = UrlFetchApp.fetch(fetchUrl, params);
var json = JSON.parse(response.getContentText());
var dataSet = json.results;
Logger.log(dataSet);
return dataSet;
}

In your script, for example, how about the following modification?
From:
function function() {
var dimensions = ["CN","IN","NZ","US","UK","MY","SG","JP","KR","DE"];
var counts = dimensions.map(dim => [getValue(dim)]);
}
To:
function sample() {
var dimensions = ["CN","IN","NZ","US","UK","MY","SG","JP","KR","DE"];
var date = new Date();
var counts = dimensions.map(dim => [date, dim, getValue(dim)[0].aggregations[0].value]);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // Please set the sheet name.
sheet.getRange(sheet.getLastRow() + 1, 1, counts.length, counts[0].length).setValues(counts);
}
function cannot be used as the function name.
From your showing log, I thought that your expected value from the URL might be dataSet[0].dimensions[0].value].
From ive removed the detail for the API call, I cannot see your whole script. In your script, fetch is used in a loop. For example, when you use fetchAll method, the process cost might be able to be reduced. But, in this case, it is required to also modify getValue function.
If you want to use this script as the custom function, how about the following script? By this, you can retrieve the result values by putting =sample() to a cell.
function sample() {
var dimensions = ["CN","IN","NZ","US","UK","MY","SG","JP","KR","DE"];
var date = new Date();
var counts = dimensions.map(dim => [date, dim, getValue(dim)[0].aggregations[0].value]);
return counts;
}

Related

Automate Hyperlink Creations

I'm trying to automate hyperlink creations on my GSheet.
Here's my script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C");
var ticketID = range.getValue();
Logger.log(ticketID);
for(i = 0; i < ticketID.length; i++){
if(ticketID.length === 0){
ticketID.setValue('')
} else if(ticketID.length > 4){
ticketID.setValue('=HYPERLINK' + '("https://mylink.com/'+ticketID+'";'+ticketID+')');
}
}
}
It does nothing but when I change ticketID.setValue by sheet.getRange("C2:C").setValue it put the whole range in the url. We can see with Logger.log(ticketID) that the whole range is selected.
So according to this result, i'm missing how to get the value of each cell individualy in the range and then check if they are long enought to create an individual url. Do I need to use something like range[i] somewhere? I'm lost.
I believe your goal as follows.
You want to retrieve the values from the cells "C2:C".
When the length of value is more than 4, you want to create a formula of HYPERLINK.
When the length of value is less than 4, you don't want to put the formula.
You want to put the formulas to the cells "C2:C".
Modification points:
When range of var range = sheet.getRange("C2:C") is used, the value of var ticketID = range.getValue() is the value of cell "C2". When you want to retrieve values from the cells "C2:C", please use getValues instead of getValue.
In this case, the retrieved value is 2 dimensional array.
When range.getValue() is the string value, ticketID of var ticketID = range.getValue() is also the string. So I think that when ticketID.setValue('##') is run, an error occurs.
In your script, setValue is used in a loop. In this case, the process cost will become high.
And, when sheet.getRange("C2:C" + sheet.getLastRow()) is used instead of sheet.getRange("C2:C"), the process cost will become low a little.
When above points are reflected to your script, it becomes as follows.
Modified script:
function ticketURLGenerator() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
var range = sheet.getRange("C2:C" + sheet.getLastRow());
var ticketIDs = range.getValues();
var values = ticketIDs.map(([c]) => [c.toString().length > 4 ? `=HYPERLINK("https://mylink.com/${c}";"${c}")` : c]);
range.setValues(values);
}
In this modification, the values are retrieved from the cells of "C2:C" + sheet.getLastRow(), and an array including the formulas and values is created, and then, the array is put to the cells.
And I used the template literal for creating the formula.
Note:
In this case, please use this script with enabling V8 runtime.
References:
getLastRow()
getValues()
map()
Template literals
You just need to apply the HYPERLINK operation to the tickets that their length is more than 4. To achieve that, you can use map() to iterate over all the elements in your list.
Solution:
function ticketURLGenerator() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
const ticketR = sheet.getRange("C2:C"+sheet.getLastRow());
const ticketIDs = ticketR.getDisplayValues().flat();
const hLinks = ticketIDs.map(ti=>{
if(ti.length>4) {
return [`=HYPERLINK("https://mylink.com/${ti}"; ${ti})`]}
else {return [ti]}
})
ticketR.setValues(hLinks);
}

How to push array position in the array faster

I have a google spreadsheet data in form of array. Now I want to push the array position I used for loop which is working fine on small data but when the data length is increased it result in delay.
Is there a faster way to push the array position in the array.
Here is the code which I am currently using:-
var ss = SpreadsheetApp.openById('19zxxxxxxxxxxxxxxxxxxxxxxxxOI');
var sheet1 = ss.getSheetByName('Sheet2');
var data = sheet1.getRange("A:H").getValues();
var email = Session.getActiveUser().getEmail();
for(var i = 0; i < data.length; i++)
{
data.unshift(i+1);
} // this for loop takes too much time.
data = data.filter(function(item){return item[7] == email});
var x = data.map(function(val){
return val.slice(0, -7);
})
Logger.log(x)
return x;
}
I believe your goal as follows.
If data.unshift(i+1) is data[i].unshift(i+1) as TheMaster's comment, you want to retrieve the values of the column "A" when the value of column "G" is the same with email. At that time, you want to add the row number to the 1st index of the row value.
From your script, I understood like this.
You want to reduce the process cost of this situation.
For this problem, how about this solution?
Pattern 1:
In this pattern, your script is modified. In this case, the result values are retrieve by one loop.
Sample script:
function myFunction() {
var ss = SpreadsheetApp.openById('19zxxxxxxxxxxxxxxxxxxxxxxxxOI');
var sheet1 = ss.getSheetByName('Sheet2');
var data = sheet1.getRange("A1:H" + sheet1.getLastRow()).getValues(); // Modified
var email = Session.getActiveUser().getEmail();
const res = data.reduce((ar, [a,,,,,,g], i) => { // Modified
if (g == email) ar.push([i + 1, a]);
return ar;
}, []);
Logger.log(res)
return res;
}
Pattern 2:
In this pattern, as other method, TextFinder and Sheets API are used. In this case, the size of base data by searching email with TextFinder can be reduced. And each values are retrieved by one API call using Sheets API.
Sample script:
Before you use this script, please enable Sheets API at Advanced Google services.
function myFunction() {
const spreadsheetId = '19zxxxxxxxxxxxxxxxxxxxxxxxxOI';
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheet = ss.getSheetByName('Sheet2');
const email = Session.getActiveUser().getEmail();
// 1. Retrieve the ranges of rows by searching "email" at the column "G".
const ranges = sheet.getRange("G1:G" + sheet.getLastRow()).createTextFinder(email).findAll();
// 2. Create an object for using with Sheets API.
const reqs = ranges.reduce((o, e) => {
const row = e.getRow();
o.rows.push(row);
o.ranges.push("A" + row);
return o;
}, {rows: [], ranges: []});
// 3. Retrieve values and add the row number.
const res = Sheets.Spreadsheets.Values.batchGet(spreadsheetId, {ranges: reqs.ranges})
.valueRanges
.map((e, i) => ([reqs.rows[i], e.values[0][0]]));
Logger.log(res)
return res;
}
If email is included other string, please use matchEntireCell(true) to TextFinder.
References:
reduce()
Advanced Google services
Method: spreadsheets.values.batchGet

Can I use custom js function for FILTER cell function in Google Sheets

I'm trying to use custom js function from the script as condition for FILTER cell function in Google Sheets.
Example: =FILTER(A1:A25; colorsame(A1:A25; $A$10))
colorsame returns true/false (one value or array, based on range size).
The custom function works well alone, like =colorsame(A1:A25; $A$10) fill the column. But in =FILTER() above I always have this error:
FILTER has mismatched range sizes. Expected row count: 25. column count: 1. Actual row count: 1, column count: 1.
I've tried googling this problem, but only got solutions for FILTER based on built-in google sheets functions. Also I try to intergrate this function with Filter and Conditional Formatting "custom formula" field, but without success.
Custom functions code:
color returns the background color(s) of provided cell or range.
function color(input) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get 'input' address by metascanner
var formula = ss.getActiveRange().getFormula();
var rangeA1Notation = formula.match(/\((.+)\)/).pop();
var cell = ss.getRange(rangeA1Notation);
var bg = cell.getBackgrounds();
return bg;
}
colorsame returns the truth table, which shows is the background color of the input range is the same as background color of color cell.
function colorsame(input, color) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get 'input' address by metascanner
var formula = ss.getActiveRange().getFormula();
var matches = formula.match(/\((.+);(.+)\)/);
var rangeA1Notation = matches[1];
var colorA1Notation = matches[2].trim();
var color = ss.getRange(colorA1Notation).getBackground();
var bgs = ss.getRange(rangeA1Notation).getBackgrounds();
var truthTable = bgs.map(function(bg) { return bg == color });
return truthTable;
}
It could be useful to see the code of your colorsame function, but meanwhile, have you tried with
=ArrayFormula(FILTER(A1:A25; colorsame(A1:A25; $A$10)))
You can return array of boolean values from custom function, which will indicate what cells will be shown after filtering.
My problem was the try to parse cell content with regexp to retrieve cell address. It works for formulas like =colorsame(A1:A25; $A$10), but not for using inside FILTER.
This version works, exclusively with FILTER:
function colorsame(input, color) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get 'input' address by metascanner
var formula = ss.getActiveRange().getFormula();
var matches = formula.match(/colorsame\((.+?);(.+?)\)/);
var rangeA1Notation = matches[1].trim();
var colorA1Notation = matches[2].trim();
var color = ss.getRange(colorA1Notation).getBackground();
var isSame = function(col) { return col === color; };
var bgs = ss.getRange(rangeA1Notation).getBackgrounds();
var truthTable = bgs.map(function(bg) { return bg.map(isSame); });
return truthTable;
}

excel-JS excel left() worksheet function not working

When I am trying to use the Excel left() worksheet function in visual studio Excel-JS api, it does not work at all! this means there is no error message, there is no value and not a clue what is not working
I am talking about this statement:
onzin = ctx.workbook.functions.left(Acode.values.toString(), 7);
onzin.load("values","address");
According to the (incredibly incomplete) documentation from Microsoft https://learn.microsoft.com/en-us/javascript/api/excel/excel.worksheetcollection?view=office-js
you have to load the variable in the Excel Object model before you use it and after a synchronization of the context, the values are available. But in my 2nd promise I loaded the values and the address of "onzin" but when I want to assign the values to the ICcode Range, it does not do it and furthermore, when I put a breakpoint in it, it seams to be empty and no errors.
Here is my code:
Excel.run(function (ctx) {
//always use the Data sheet
var MyDataSheet = ctx.workbook.worksheets.getItem("Data");
var ConfigSheet = ctx.workbook.worksheets.getItem("Config");
var onzin;
MyDataSheet.activate();
var productStartRange = MyDataSheet.getUsedRange();
//define a range before the values can be loaded to the Excel Object model
var Acode = MyDataSheet.getRange("A3").load("values, address");
productStartRange.load("values, address, length");
// Run the queued-up commands, and return a promise to indicate task completion
return ctx.sync()
.then(function () {
var myBounds = GetBounds(productStartRange);
ConfigSheet.activate();
//put the column and row bounds in the config sheet
var ColBounds = ConfigSheet.getRange("B22");
var RowBounds = ConfigSheet.getRange("B21");
var NumProducts = ConfigSheet.getRange("B34");
var NumProperties = ConfigSheet.getRange("B27");
//ICcode.values = ctx.workbook.functions.left(Acode.values.toString(), 7);
onzin = ctx.workbook.functions.left(Acode.values.toString(), 7);
onzin.load("values","address");
//ICcode.values = Acode.values.toString().substring(0, 7);
//ICcode.values = onzin.values;
ColBounds.values = myBounds.LastCol;
RowBounds.values = myBounds.LastRow - 1;
//total number of products
NumProducts.values = RowBounds.values - 2;
//total number of properties
NumProperties.values = ColBounds.values - 2;
//load the products from the Data source sheet into one range
var ProductRange = MyDataSheet.getRangeByIndexes(3, 1, myBounds.LastRow, 3);
ProductRange.load("values");
})
.then(ctx.sync)
.then(function () {
var ICcode = ConfigSheet.getRange("B36");
ICcode = onzin.values;
//var Mystring = rowAddress.address;
showNotification("onzin waardes: ", onzin.values);
var PropSheet = ctx.workbook.worksheets.getItem("PropertySelection");
PropSheet.activate();
});
}).catch(errorHandler);
I would expect that the worksheet function takes the first 7 characters from the cell value of "Acode" and writes it to the range ICcode on location B36.
any help would be appreciated
Deleted my earlier answer because I misread part of your code.
This line of your code has two problems:
onzin.load("values","address");
There should not be an "s" on the first string. It is just "value". Also, remove the "s" from the line ICcode = onzin.values;.
There is no "address" property on onzin object. (The Excel.Range object does have properties named values and address, which is why I thought in my original answer that you were treating onzin as an Excel.Range object.)
I was able to spot the errors by reading this article in the official documentation: Call built-in Excel worksheet functions. This article is the very first search result in both Bing and Google if you search for "office add-ins worksheet functions". So, I gently don't agree with you that the documentation is "incredibly incomplete".
Will you try this piece, it works on my side.
var Acode = sheet.getRange("B2").load("values, address");
await context.sync();
console.log(Acode);
var onzin = context.workbook.functions.left(Acode.values.toString(), 3);
var substring = Acode.values.toString().substring(0,7);
console.log(Acode.values.toString());
console.log(substring);

How to take a single cell and a range in a custom function and take the range data in an array in google sheets

Firstly, I am very new here. Searched the whole internet about this and finally landing a question here.
function StockCoverW(stock,range){
var x = stock;
var r = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var r1 = SpreadsheetApp.getActiveSheet().getNamedRanges(range);
}
Here, stock is a single cell for example: A1
and range is a range of cell for example: A2:E2
so the function call will be StockCoverW(A1,A2:E2)
now I need to take the values from the range cells into an Array.
Obviously the code here is not correct as cannot debug or do anything.
How to implement this in google spreadsheet script?
Change getDataRange (which grabs all valued on sheet) to getRange(range)
function StockCoverW(stock,range){
var x = stock;
var r = SpreadsheetApp.getActiveSheet().getRange(range).getValues();
var r1 = SpreadsheetApp.getActiveSheet().getNamedRanges(range);
}
Note though that the function doesn’t return or alter anything so when it runs it just gets the valued and the named ranges.
Also note that the array you get with values is a 2d array and if you want it to be just a simple array then use
function StockCoverW(stock,range){
var x = stock;
var r = SpreadsheetApp.getActiveSheet().getRange(range).getValues()[0];
var r1 = SpreadsheetApp.getActiveSheet().getNamedRanges(range);
}

Categories

Resources