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);
}
Related
I am trying to run a script that validates a table of stagnant data against several criteria.
I am fairly new to using scripts, and I spent most of yesterday gaining a base knowledge but I am still struggling.The first objective I want to achieve is checking dates - I want to check the dates in one column, row by row, and if any of the dates are greater than the corresponding date two columns over I want either the whole row or just the second column value to return when I run the script. I think my main problem is that I still cannot fully grasp how to use the return function. So far I am only able to return the whole data set, not just the results of my if statement.
function dateValidation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var values = sheet.getDataRange().getValues();
var cell = sheet.getRange(1,21,5000,1) //first date column
var cell2 = sheet.getRange (1,23,5000,1) //second date column
var cell3 = sheet.getRange (1,2,5000,1) //column to return
for(var i = 1; i < values.length; i++) {
if(cell.getValues() > cell2.getValues())
return cell3.getValues();
}
I have a feeling I may be going about this all wrong with the if statement. I typically use formulas like extended queries but for this, I need a function to adhere to a button to be able to validate the data on a whim, not have it constantly update in a query. Any help would be greatly appreciated, but I know I still have a lot to learn.
I tried to replicate your code and found some issues.
using values.length in your for loop might introduce index out of bounds once the number of rows in your valuesis not the same with the number of rows indicated in the 3rd parameter of getRange(1,21,5000,1).
The return of getValues() is 2 dimensional array. You have to indicate index of array you want to check to be able to access the data inside the array.
Using return cell3.getValues(); statement inside for loop will just return the whole values of cell3 once the condition is met.
To fix the issues:
Use values.length as third parameter of cell,cell2,cell3 getRange().
Each Array inside the 2d array represents a rows of data. Since we only need 1 column for cell, cell2, and cell3 we can indicate array[i][0] to access the first element of each array.
Instead of using return, create a empty array above the for loop and use push() to store the data that matches the condition.
Example:
Code:
function dateValidation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var values = sheet.getDataRange().getValues();
var cell = sheet.getRange(1,1,values.length,1).getValues(); //first date column
var cell2 = sheet.getRange (1,2,values.length,1).getValues(); //second date column
var cell3 = sheet.getRange (1,3,values.length,1).getValues(); //column to return
var single_col = [];
var whole_row = [];
for(var i = 1; i < cell.length; i++) {
if(cell[i][0] > cell2[i][0])
{
single_col.push(cell3[i][0]);
whole_row.push(values[i]);
}
}
Logger.log(single_col);
Logger.log(whole_row);
//choose either of these two below.
//return single_col;
//return whole_row;
}
Output:
References:
Javascript Return
getRange()
getValues()
Array.prototype.push()
first: I really tried hard to get along, but I am more a supporter than a programmer.
I put some Text in Google Calc and wanted to check the amount of the occurances of "Mueller, Klaus" (It appears 5 times within the data range). The sheet contains 941 rows and 1 Column ("A").
Here is my code to find out:
function countKlaus() {
// Aktives Spreadsheet auswählen
var ss = SpreadsheetApp.getActiveSpreadsheet();
// Aktives Tabellenblatt auswählen
var sheet = ss.getSheetByName("Tabellenblatt1");
var start = 1;
var end = sheet.getLastRow();
var data = sheet.getRange(start,1,end,1).getValues();
var curRow = start;
var cntKlaus = 0;
for( x in data )
{
var value = daten[x];
//ui.alert(value);
if(value.indexOf("Mueller, Klaus")> -1){
cntKlaus = cntKlaus + 1;
}
}
ui.alert(cntKlaus);
}
The result message is "0" but should be "5".
Issues:
You are very close to the solution, except for these two issues:
daten[x] should be replaced by data[x].
ui.alert(cntKlaus) should be replaced by SpreadsheetApp.getUi().alert(cntKlaus).
Solution (optimized by me) - Recommended:
function countKlaus() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Tabellenblatt1");
const cntKlaus = sheet
.getRange('A1:A' + sheet.getLastRow())
.getValues()
.flat()
.filter(r=>r.includes("Mueller, Klaus"))
.length;
SpreadsheetApp.getUi().alert(cntKlaus);
}
You can leave out this term + sheet.getLastRow() since we are filtering on a non-blank value. But I think it will be faster to have less data to use filter on in the first place.
References:
flat : convert the 2D array to 1D array.
filter : filter only on "Mueller, Klaus".
Array.prototype.length: get the length of the filtered data
which is the desired result.
includes: check if Mueller, Klaus is included in the text.
Bonus info
Just for your information, my solution can be rewritten in one line of code if that's important to you:
SpreadsheetApp.getUi().alert(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tabellenblatt1").getRange('A1:A').getValues().flat().filter(r=>r.includes("Mueller, Klaus")).length);
I have rows of data in column A containing cells starting with AR. I would like any cell that contains AR to be deleted. I have script already but this only deletes exact matches
So example is AR12345 in Column A & A12345. So it should ONLY delete the cell row with AR and not just A
function DeleteAny() {
var sheet = SpreadsheetApp.getActive();
sheet.setActiveSheet(sheet.getSheetByName('MULTI KIT DATA'), true);
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
// I cant put AR here because it wont delete anything. the AR numbers keep changing also
if (row[14] == '') {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
};
I have searched but cannot find anything.
How about using javascript Array#filter ? This is probably simplest for simple data.
// filter values
let values = sheet.getDataRange().getValues()
.filter(row => !row.find(v => v.match(/^AR/)))
// clear range
sheet.getDataRange().clear()
// write back filtered values
sheet.getRange(1, 1, values.length, values[0].length).setValues(values)
How about the following modification?
Pattern 1:
In this pattern, as a simple modification, your script is modified.
From:
if (row[14] == '') {
To:
if (row[0].length > 1 && row[0].substr(0, 2) == "AR") {
In this modification, the top 2 characters are retrieved with row[0].substr(0, 2).
If AR is included in the inner value, if (row[0].length > 1 && row[0].includes("AR")) { might be suitable.
Note:
In your script, row[14] is used. But in your question, the values are in the column "A". So I used row[0]. If you want to check other column, please modify it.
Pattern 2:
In this pattern, as other sample script, TextFinder and Sheets API are used. When you use this, please enable Sheets API at Advanced Google services.
function myFunction() {
const sheetName = "MULTI KIT DATA"; // Please set the sheet name.
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(sheetName);
const sheetId = sheet.getSheetId();
const requests = sheet
.getRange(`A1:A${sheet.getLastRow()}`)
.createTextFinder("^AR")
.matchCase(true)
.useRegularExpression(true)
.findAll()
.map(r => r.getRow())
.reverse()
.map(r => ({deleteDimension:{range:{sheetId:sheetId,startIndex:r - 1,endIndex:r,dimension:"ROWS"}}}));
Sheets.Spreadsheets.batchUpdate({requests: requests}, ss.getId());
}
In this sample script, the ranges of values which have AR at the top 2 characters are retrieved with TextFinder. And the request body is created using the retrieved ranges, and then, it requests to Sheets API with the request body. By this, the rows you want to delete are deleted.
When there are a lot of rows you want to delete, the process cost of this sample script might be low.
References:
substr()
includes()
TextFinder
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
I have thousands of rows of data in a Google Sheets File in a column that looks something like
[{"amountMax":49.99,"amountMin":49.99,"availability":"true","color":"Brown","currency":"USD","dateSeen":["2019-04-11T08:00:00Z"],"isSale":"false","offer":"Online only","sourceURLs":["https://www.walmart.com/ip/SadoTech-Model-CXR-Wireless-Doorbell-1-Remote-Button-2-Plugin-Receivers-Operating-500-feet-Range-50-Chimes-Batteries-Required-Receivers-Beige-Fixed-C/463989633"]}]
I would like to be able to return the max value, the currency, the color attributes. How can I do that in Google Sheets. Ideally would like to do something like being able to retrieve the data attributes how I would normally in javascript like in this link here https://repl.it/#alexhoy/WetSlateblueDribbleware
However this does not seem to work for me when creating a function in script.google.com
For example, here is a slugify function which takes an input (cell) and turns it into a slug/handle without the need for looping. In Google Sheets I can then call =slugify(b2) and turn that value into slug form
/**
* Converts value to slug
* #customfunction
*/
function slugify(value) {
/*
* Convert the the vs in a range of cells into slugs.
* #customfunction
*/
let slug = '';
slug = value.substring(0, 100).toLowerCase();
slug = slug.replace(/[^\w\s-]/g, '');
slug = slug.replace(/\s+/g, '-');
Logger.log(slug);
return slug;
}
I want to do the same thing without looping to parse the object data above or declaring a range of values and what not.
Any suggestions on how I can do this in a simple way like shown above without the need for declaring active spreadsheet, range values and looping.
The following script will give you an idea about how to approach this task.
It assumes that:
the json data described in your question is in Cell A2.
the max value will be inserted into cell D2
the currency will be inserted into cell E2
the color will be inserted into cell F2
The script uses temporary arrays to capture the values and then assign it to a 2d array.
If you have many rows of data, then you will need to create a loop. I suggest that you build the arraydata progressively, and only update the target range at the end of the loop. This will give you the most efficient outcome.
function so6031098604() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var content = JSON.parse(sheet.getRange("A2").getValue());
// temp arrar to capture the data
var temparray = [];
temparray.push(content[0]["amountMax"]);
temparray.push(content[0]["currency"]);
temparray.push(content[0]["color"]);
// second array to accept the row data
var arraydata =[];
arraydata.push(temparray)
// define the target range
var targetrange = sheet.getRange(2, 4, 1, 3);
// update with the arraydata
targetrange.setValues(arraydata);
}
You want a custom function that will return certain fields from a JSON array.
In the following example, the target cell can be a single cell or an array.
This example does not use an arrayformula. The mechanics of using an arrayformula with a custom function may be something that you can research here Custom SHEETNAME function not working in Arrayformula.
Note: A 30 second quota applies to the execution of a Custom function
/**
* gets the MaxAmount, Current and Color from the data
*
* #param {cell reference or range} range The range to analyse.
* #return amountMax,currency and color
* #customfunction
*/
function getJsonData(range) {
//so6031098606
// Test whether range is an array.
if (range.map) {
// if yes, then loop through the rows and build the row values
var jsonLine = [];
for (var i = 0; i < range.length; i++) {
var jsonValues=[];
var v = JSON.parse(range[i][0]);
jsonValues.push(v.amountMax);
jsonValues.push(v.currency);
jsonValues.push(v.color);
// aggregate the row values
jsonLine.push(jsonValues);
} // end i
return jsonLine;
} else {
// if no, then just return a single set of values
var v = JSON.parse(range);
var jsonValues = [];
jsonValues.push(v.amountMax);
jsonValues.push(v.currency);
jsonValues.push(v.color);
return [jsonValues];
}
}
I am building a script to copy a row in another spreadsheet. The idea is to have one sheet with the inputs that are going to be stored in a second spreadsheet. However, I am facing some real struggle in building the dynamic paste range. This is the point I was able to reach with my present knowledge:
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopy = source.getRange('bb').getValues();
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange('bb').setValues(dataToCopy)
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('yyy').getRange('bb');
Clean.clear({contentsOnly:true});
}
This script copy a range into a fixed range in a second spreadsheet, and it clears the values present in the source. My question is: How can I create a range that makes the script paste the data in the first blank row in the second spreadsheet?
I tried some combination of appendRow, getLastRow, insertRowAfter, but I was not able to get it done.
Thank you for your time!
This is what I was able to achieve with the help of a friend:
function Export2() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("1UfKqXCMNIbjh3ge7s26SNkXyGez-bY3fvl6_3-RQKos");
var source = ss.getSheetByName('Sheet26');
var dataToCopy = source.getRange('A1:E1');
var copyToSheet = database.getSheetByName("TOT");
var lastRow = copyToSheet.getLastRow();
for (var i = 1; i<6 ;i++){
var Paste = copyToSheet.getRange(lastRow + 1,i).setValue(dataToCopy.getCell(1, i).getValue());
}
var Clean = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet26').getRange('A1:E1');
Clean.clear({contentsOnly:true});
}
Below is a script that will do the thing you want to do dynamically without the use of named ranges. It assumes that all the data on the source sheet should be copied over to the destination sheet. Let me know if you need any additional explanation beyond what is provided in the comments.
function Export() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.openById("xxx");
var source = ss.getSheetByName('yyy');
var dataToCopyRng = source.getDataRange(); //Gets range object of all data on source sheet
var dataToCopy = dataToCopyRng.getValues(); //Gets the values of the source range in a 2 dimensional array
var copyToSheet = database.getSheetByName("zzz");
var copyData = copyToSheet.getRange(copyToSheet.getLastRow()+1,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy)
//Explination of the above command is as follows getRange(row, column, numRows, numColumns)
//--row is copyToSheet.getLastRow()+1 -- finds last row with content and adds one
//--Column is 1 for Column A
//--numRows is the length of the array (how many rows are in the array of values)
//--numcolumns is the length of the first element of the 2 dimensional array (arrays start at zero). The length of the first element is how many columns are in the array
//--combine the above with .getRange and you get the range object that is an exact match to the array of source data rows and columns
//--then you simply set the values of this range with the source data
dataToCopyRng.clear({contentsOnly:true});
}
You can learn more about 2 dimensional arrays here.
Cheers!